Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4ab0bfa
feat(piece-cleanup): add config, entity columns, and env vars for pie…
Chaitu-Tatipamula Mar 10, 2026
abd9053
feat(piece-cleanup): implement core cleanup service and jobs integration
Chaitu-Tatipamula Mar 10, 2026
e5c87c4
refactor: optimize piece cleanup job execution and fix timeout leak
Chaitu-Tatipamula Mar 10, 2026
72b22ce
fix(piece-cleanup): address PR review feedback on cleanup service
Chaitu-Tatipamula Mar 11, 2026
3d829a8
chore(db): add migration for piece cleanup columns
Chaitu-Tatipamula Mar 11, 2026
6412afd
docs: document piece_cleanup job
Chaitu-Tatipamula Mar 11, 2026
60a4b7c
refactor(piece-cleanup): remove fileSize fallback from bytesRemoved c…
Chaitu-Tatipamula Mar 11, 2026
cff1223
test(jobs): add default isProviderOverQuota mock to prevent "... is n…
Chaitu-Tatipamula Mar 11, 2026
b45afa1
feat(piece-cleanup): use live provider storage for quota decisions
Chaitu-Tatipamula Mar 18, 2026
cb5bc81
fix(piece-cleanup): align with synapse-sdk integration
Chaitu-Tatipamula Mar 23, 2026
2cbe7bb
fix: resolve rebase conflicts with synapse-sdk and piece-cleanup inte…
Chaitu-Tatipamula Apr 6, 2026
7575aba
fix: address PR review feedback for piece-cleanup
Chaitu-Tatipamula Apr 6, 2026
9511310
style: standardize remaining log fields in piece-cleanup service
Chaitu-Tatipamula Apr 6, 2026
34986af
refactor: remove unused formatBytes and StorageContext
Chaitu-Tatipamula Apr 6, 2026
0fad355
fix: scope cleanup queries to wallet and extract shared quota check
Chaitu-Tatipamula Apr 7, 2026
cd5876b
feat: add PieceCleanupLogContext and thread it through cleanup flow
Chaitu-Tatipamula Apr 9, 2026
8a13e30
refactor: accept ProviderJobContext at cleanup public API boundary
Chaitu-Tatipamula Apr 9, 2026
fee6042
fix: fixes code quality CI
Chaitu-Tatipamula Apr 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions apps/backend/src/common/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,16 @@ export type JobLogContext = {
providerId?: bigint;
providerName?: string;
};

/**
* Structured logging context for piece cleanup operations
*/
export type PieceCleanupLogContext = {
jobId?: string;
providerAddress: string;
providerId?: bigint;
providerName?: string;
storedBytes?: number;
thresholdBytes?: number;
targetBytes?: number;
};
56 changes: 56 additions & 0 deletions apps/backend/src/config/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,30 @@ export const configValidationSchema = Joi.object({
DATA_SET_CREATION_JOB_TIMEOUT_SECONDS: Joi.number().min(60).default(300), // 5 minutes max runtime for dataset creation jobs
IPFS_BLOCK_FETCH_CONCURRENCY: Joi.number().integer().min(1).max(32).default(6),

// Piece Cleanup
MAX_DATASET_STORAGE_SIZE_BYTES: Joi.number()
.integer()
.min(1)
.default(24 * 1024 * 1024 * 1024), // 24 GiB per SP
TARGET_DATASET_STORAGE_SIZE_BYTES: Joi.number()
.integer()
.min(1)
.default(20 * 1024 * 1024 * 1024) // 20 GiB per SP
.custom((value, helpers) => {
const max = helpers.state.ancestors?.[0]?.MAX_DATASET_STORAGE_SIZE_BYTES;
if (max != null && value >= max) {
return helpers.error("any.invalid", {
message: `TARGET_DATASET_STORAGE_SIZE_BYTES (${value}) must be less than MAX_DATASET_STORAGE_SIZE_BYTES (${max})`,
});
}
return value;
}, "target < max validation"),
JOB_PIECE_CLEANUP_PER_SP_PER_HOUR: Joi.number()
.min(0.001)
.max(20)
.default(1 / 24), // ~once per day
MAX_PIECE_CLEANUP_RUNTIME_SECONDS: Joi.number().min(60).default(300), // 5 minutes max runtime for cleanup jobs

// Dataset
DEALBOT_LOCAL_DATASETS_PATH: Joi.string().default(DEFAULT_LOCAL_DATASETS_PATH),
RANDOM_PIECE_SIZES: Joi.string().default("10485760"), // 10 MiB
Expand Down Expand Up @@ -239,6 +263,20 @@ export interface IJobsConfig {
* Uses AbortController to actively cancel job execution.
*/
retrievalJobTimeoutSeconds: number;
/**
* Target number of piece cleanup runs per storage provider per hour.
*
* Increasing this makes cleanup more aggressive at the cost of more SP API calls.
* Only used when `DEALBOT_JOBS_MODE=pgboss`.
*/
pieceCleanupPerSpPerHour: number;
/**
* Maximum runtime (seconds) for piece cleanup jobs before forced abort.
*
* Uses AbortController to actively cancel job execution.
* Only used when `DEALBOT_JOBS_MODE=pgboss`.
*/
maxPieceCleanupRuntimeSeconds: number;
}

export interface IDatasetConfig {
Expand All @@ -258,6 +296,11 @@ export interface IRetrievalConfig {
ipfsBlockFetchConcurrency: number;
}

export interface IPieceCleanupConfig {
maxDatasetStorageSizeBytes: number;
targetDatasetStorageSizeBytes: number;
}

export interface ISpBlocklistConfig {
/** Provider numeric IDs to block from all scheduled checks. */
ids: Set<string>;
Expand All @@ -274,6 +317,7 @@ export interface IConfig {
dataset: IDatasetConfig;
timeouts: ITimeoutConfig;
retrieval: IRetrievalConfig;
pieceCleanup: IPieceCleanupConfig;
spBlocklists: ISpBlocklistConfig;
}

Expand Down Expand Up @@ -346,6 +390,8 @@ export function loadConfig(): IConfig {
dealJobTimeoutSeconds: Number.parseInt(process.env.DEAL_JOB_TIMEOUT_SECONDS || "360", 10),
retrievalJobTimeoutSeconds: Number.parseInt(process.env.RETRIEVAL_JOB_TIMEOUT_SECONDS || "60", 10),
dataSetCreationJobTimeoutSeconds: Number.parseInt(process.env.DATA_SET_CREATION_JOB_TIMEOUT_SECONDS || "300", 10),
pieceCleanupPerSpPerHour: Number.parseFloat(process.env.JOB_PIECE_CLEANUP_PER_SP_PER_HOUR || String(1 / 24)),
maxPieceCleanupRuntimeSeconds: Number.parseInt(process.env.MAX_PIECE_CLEANUP_RUNTIME_SECONDS || "300", 10),
},
dataset: {
localDatasetsPath: process.env.DEALBOT_LOCAL_DATASETS_PATH || DEFAULT_LOCAL_DATASETS_PATH,
Expand Down Expand Up @@ -375,6 +421,16 @@ export function loadConfig(): IConfig {
retrieval: {
ipfsBlockFetchConcurrency: Number.parseInt(process.env.IPFS_BLOCK_FETCH_CONCURRENCY || "6", 10),
},
pieceCleanup: {
maxDatasetStorageSizeBytes: Number.parseInt(
process.env.MAX_DATASET_STORAGE_SIZE_BYTES || String(24 * 1024 * 1024 * 1024),
10,
),
targetDatasetStorageSizeBytes: Number.parseInt(
process.env.TARGET_DATASET_STORAGE_SIZE_BYTES || String(20 * 1024 * 1024 * 1024),
10,
),
},
spBlocklists: {
ids: parseIdList(process.env.BLOCKED_SP_IDS),
addresses: parseAddressList(process.env.BLOCKED_SP_ADDRESSES),
Expand Down
7 changes: 7 additions & 0 deletions apps/backend/src/database/entities/deal.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ export class Deal {
@Column({ name: "retry_count", default: 0 })
retryCount: number;

// Piece cleanup tracking
@Column({ name: "cleaned_up", default: false })
cleanedUp: boolean;

@Column({ name: "cleaned_up_at", type: "timestamptz", nullable: true })
cleanedUpAt: Date | null;

@CreateDateColumn({ name: "created_at", type: "timestamptz" })
createdAt: Date;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export type JobType =
| "metrics"
| "metrics_cleanup"
| "providers_refresh"
| "data_retention_poll";
| "data_retention_poll"
| "piece_cleanup";

@Entity("job_schedule_state")
@Index("job_schedule_state_job_type_sp_unique", ["jobType", "spAddress"], { unique: true })
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { MigrationInterface, QueryRunner } from "typeorm";

export class AddPieceCleanupColumns1761500000002 implements MigrationInterface {
name = "AddPieceCleanupColumns1761500000002";

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE deals
ADD COLUMN IF NOT EXISTS cleaned_up BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN IF NOT EXISTS cleaned_up_at TIMESTAMPTZ NULL
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE deals DROP COLUMN IF EXISTS cleaned_up_at`);
await queryRunner.query(`ALTER TABLE deals DROP COLUMN IF EXISTS cleaned_up`);
}
}
2 changes: 2 additions & 0 deletions apps/backend/src/jobs/jobs.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { StorageProvider } from "../database/entities/storage-provider.entity.js
import { DealModule } from "../deal/deal.module.js";
import { MetricsModule } from "../metrics/metrics.module.js";
import { MetricsWorkerModule } from "../metrics/metrics-worker.module.js";
import { PieceCleanupModule } from "../piece-cleanup/piece-cleanup.module.js";
import { RetrievalModule } from "../retrieval/retrieval.module.js";
import { WalletSdkModule } from "../wallet-sdk/wallet-sdk.module.js";
import { JobsService } from "./jobs.service.js";
Expand All @@ -25,6 +26,7 @@ const metricsModule = runMode === "worker" ? MetricsWorkerModule : MetricsModule
metricsModule,
WalletSdkModule,
DataRetentionModule,
PieceCleanupModule,
],
providers: [JobsService, JobScheduleRepository],
})
Expand Down
Loading
Loading