velocious 1.0.518 → 1.0.519
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/build/background-jobs/main.js +31 -0
- package/build/background-jobs/store.js +90 -3
- package/build/configuration-types.js +18 -0
- package/build/configuration.js +17 -2
- package/build/database/record/attachments/handle.js +18 -0
- package/build/database/record/attachments/store.js +45 -0
- package/build/http-server/client/index.js +1 -1
- package/build/src/background-jobs/main.d.ts +12 -0
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +32 -1
- package/build/src/background-jobs/store.d.ts +37 -1
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +80 -4
- package/build/src/configuration-types.d.ts +48 -0
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +18 -1
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +17 -2
- package/build/src/database/record/attachments/handle.d.ts +11 -0
- package/build/src/database/record/attachments/handle.d.ts.map +1 -1
- package/build/src/database/record/attachments/handle.js +17 -1
- package/build/src/database/record/attachments/store.d.ts +13 -0
- package/build/src/database/record/attachments/store.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.js +40 -1
- package/build/src/http-server/client/index.js +2 -2
- package/package.json +1 -1
- package/src/background-jobs/main.js +31 -0
- package/src/background-jobs/store.js +90 -3
- package/src/configuration-types.js +18 -0
- package/src/configuration.js +17 -2
- package/src/database/record/attachments/handle.js +18 -0
- package/src/database/record/attachments/store.js +45 -0
- package/src/http-server/client/index.js +1 -1
package/README.md
CHANGED
|
@@ -440,7 +440,7 @@ This creates `src/frontend-models/user.js` (and one file per configured resource
|
|
|
440
440
|
- Attribute methods like `user.name()` and `user.setName(...)`
|
|
441
441
|
- Relationship helpers (when `relationships` are configured), for example `task.project()`, `await task.projectOrLoad()`, `await project.tasks().toArray()`, `await project.tasks().load()`, and `project.tasks().build({...})`
|
|
442
442
|
- Preload relationships onto records you already have with `await record.preload(Model.preload({...}).select({...}))` (or `Preloader.preload(records, ...)` for arrays), including `selectsExtra(...)` and a `{force: true}` reload option — see [docs/frontend-models.md](docs/frontend-models.md#preloading-onto-loaded-records)
|
|
443
|
-
- Attachment helpers (when `attachments` are configured), for example `await task.descriptionFile().attach(file)`, `await task.descriptionFile().download()`, and `await task.update({descriptionFile: file})`
|
|
443
|
+
- Attachment helpers (when `attachments` are configured), for example `await task.descriptionFile().attach(file)`, `await task.descriptionFile().download()`, `await task.files().purgeAll()`, and `await task.update({descriptionFile: file})`
|
|
444
444
|
|
|
445
445
|
React components can subscribe to lifecycle broadcasts without manual cleanup code:
|
|
446
446
|
|
|
@@ -501,6 +501,14 @@ await task.update({
|
|
|
501
501
|
})
|
|
502
502
|
```
|
|
503
503
|
|
|
504
|
+
Purge a record's attachments — both the stored files and their rows — for example before destroying the owner record:
|
|
505
|
+
|
|
506
|
+
```js
|
|
507
|
+
const purgedCount = await task.files().purgeAll()
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
`purgeAll()` deletes each attachment's backing storage and then its row, and removes only the attachments that existed when the purge started (a concurrent `attach()` for the same record/name is left intact). It throws without deleting anything if a storage driver has no `delete` operation, so a driver configured without deletion can never silently leak storage. It is a no-op for unpersisted records and returns the number of attachments purged.
|
|
511
|
+
|
|
504
512
|
Configure attachment storage drivers in `Configuration`:
|
|
505
513
|
|
|
506
514
|
```js
|
|
@@ -54,6 +54,7 @@ export default class BackgroundJobsMain {
|
|
|
54
54
|
this.port = typeof port === "number" ? port : config.port
|
|
55
55
|
this.dispatchStrategy = config.dispatchStrategy
|
|
56
56
|
this.pollIntervalMs = config.pollIntervalMs
|
|
57
|
+
this.retention = config.retention
|
|
57
58
|
this.store = new BackgroundJobsStore({configuration, databaseIdentifier: config.databaseIdentifier})
|
|
58
59
|
this.logger = new Logger(this)
|
|
59
60
|
/**
|
|
@@ -88,6 +89,10 @@ export default class BackgroundJobsMain {
|
|
|
88
89
|
* Narrows the runtime value to the documented type.
|
|
89
90
|
* @type {ReturnType<typeof setTimeout> | undefined} */
|
|
90
91
|
this._orphanTimer = undefined
|
|
92
|
+
/**
|
|
93
|
+
* Narrows the runtime value to the documented type.
|
|
94
|
+
* @type {ReturnType<typeof setTimeout> | undefined} */
|
|
95
|
+
this._retentionTimer = undefined
|
|
91
96
|
/**
|
|
92
97
|
* Narrows the runtime value to the documented type.
|
|
93
98
|
* @type {BackgroundJobsScheduler | undefined} */
|
|
@@ -139,6 +144,10 @@ export default class BackgroundJobsMain {
|
|
|
139
144
|
void this._sweepOrphans()
|
|
140
145
|
}, 60000)
|
|
141
146
|
|
|
147
|
+
this._retentionTimer = setInterval(() => {
|
|
148
|
+
void this._sweepRetention()
|
|
149
|
+
}, this.retention.sweepIntervalMs)
|
|
150
|
+
|
|
142
151
|
this.scheduler = new BackgroundJobsScheduler({
|
|
143
152
|
configuration: this.configuration,
|
|
144
153
|
enqueueJob: async ({args, jobClass, options}) => {
|
|
@@ -197,10 +206,12 @@ export default class BackgroundJobsMain {
|
|
|
197
206
|
if (this._scheduledTimer) clearTimeout(this._scheduledTimer)
|
|
198
207
|
if (this._errorRetryTimer) clearTimeout(this._errorRetryTimer)
|
|
199
208
|
if (this._orphanTimer) clearInterval(this._orphanTimer)
|
|
209
|
+
if (this._retentionTimer) clearInterval(this._retentionTimer)
|
|
200
210
|
this._pollTimer = undefined
|
|
201
211
|
this._scheduledTimer = undefined
|
|
202
212
|
this._errorRetryTimer = undefined
|
|
203
213
|
this._orphanTimer = undefined
|
|
214
|
+
this._retentionTimer = undefined
|
|
204
215
|
}
|
|
205
216
|
|
|
206
217
|
/**
|
|
@@ -1085,4 +1096,24 @@ export default class BackgroundJobsMain {
|
|
|
1085
1096
|
this.logger.error(() => ["Failed to mark orphaned jobs:", error])
|
|
1086
1097
|
}
|
|
1087
1098
|
}
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* Deletes terminal job rows past their retention window so the jobs table
|
|
1102
|
+
* does not grow unbounded. Runs on its own interval; failures are logged and
|
|
1103
|
+
* retried on the next tick.
|
|
1104
|
+
* @returns {Promise<void>} - Resolves after one retention sweep.
|
|
1105
|
+
*/
|
|
1106
|
+
async _sweepRetention() {
|
|
1107
|
+
try {
|
|
1108
|
+
const deleted = await this.store.pruneTerminalJobs({
|
|
1109
|
+
completedTtlMs: this.retention.completedTtlMs,
|
|
1110
|
+
failedTtlMs: this.retention.failedTtlMs,
|
|
1111
|
+
batchSize: this.retention.batchSize
|
|
1112
|
+
})
|
|
1113
|
+
|
|
1114
|
+
if (deleted > 0) this.logger.warn(() => ["Pruned terminal background jobs", deleted])
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
this.logger.error(() => ["Failed to prune terminal jobs:", error])
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1088
1119
|
}
|
|
@@ -476,11 +476,25 @@ export default class BackgroundJobsStore {
|
|
|
476
476
|
for (const row of rows) {
|
|
477
477
|
const job = this._normalizeJobRow(row)
|
|
478
478
|
|
|
479
|
+
// Fence the reclaim on the exact handoff this sweep selected, using its
|
|
480
|
+
// `handed_off_at_ms` rather than its `handoff_id`. Two reasons:
|
|
481
|
+
// 1. Null-safe. Some rows have a null `handoff_id` (handed off by an
|
|
482
|
+
// older velocious before handoff-id fencing). `{handoff_id: null}`
|
|
483
|
+
// renders as `handoff_id = NULL`, which matches nothing, so those
|
|
484
|
+
// rows would be stranded in `handed_off` forever.
|
|
485
|
+
// 2. Race-safe. If the row is returned to the queue and re-handed-off
|
|
486
|
+
// between the SELECT above and this update, it gets a fresh
|
|
487
|
+
// `handed_off_at_ms` (always "now"), so this stale cutoff-era
|
|
488
|
+
// timestamp no longer matches and we won't fail/orphan — or
|
|
489
|
+
// wrongly release the concurrency reservation of — that new lease.
|
|
490
|
+
// `handed_off_at_ms` is always set on a handed-off row (and the SELECT
|
|
491
|
+
// required it `<= cutoff`), so it is a reliable null-safe lease pin.
|
|
479
492
|
const orphanedJob = await this._applyFailure({
|
|
480
493
|
db,
|
|
481
494
|
job,
|
|
482
495
|
error: "Job orphaned after timeout",
|
|
483
|
-
markOrphaned: true
|
|
496
|
+
markOrphaned: true,
|
|
497
|
+
conditions: {id: job.id, status: "handed_off", handed_off_at_ms: job.handedOffAtMs}
|
|
484
498
|
})
|
|
485
499
|
|
|
486
500
|
if (orphanedJob) orphanedCount += 1
|
|
@@ -490,6 +504,78 @@ export default class BackgroundJobsStore {
|
|
|
490
504
|
})
|
|
491
505
|
}
|
|
492
506
|
|
|
507
|
+
/**
|
|
508
|
+
* Deletes terminal job rows past their retention window so the jobs table
|
|
509
|
+
* does not grow unbounded (completed rows in particular accumulate forever
|
|
510
|
+
* otherwise). Batched by id — SELECT a page of ids, then
|
|
511
|
+
* `DELETE ... WHERE id IN (...)` — rather than `DELETE ... LIMIT`, which not
|
|
512
|
+
* every driver supports; each batch runs on its own connection so the sweep
|
|
513
|
+
* yields between batches instead of holding one long transaction.
|
|
514
|
+
* @param {object} [args] - Options.
|
|
515
|
+
* @param {number | null} [args.completedTtlMs] - Delete `completed` jobs whose `completed_at_ms` is older than this many ms. Falsy or `<= 0` disables completed pruning.
|
|
516
|
+
* @param {number | null} [args.failedTtlMs] - Delete terminal `failed`/`orphaned` jobs older than this many ms (by `failed_at_ms`/`orphaned_at_ms`). Falsy or `<= 0` disables.
|
|
517
|
+
* @param {number} [args.batchSize] - Max rows deleted per batch. Default `1000`.
|
|
518
|
+
* @returns {Promise<number>} - Total rows deleted.
|
|
519
|
+
*/
|
|
520
|
+
async pruneTerminalJobs({completedTtlMs = null, failedTtlMs = null, batchSize = 1000} = {}) {
|
|
521
|
+
await this.ensureReady()
|
|
522
|
+
|
|
523
|
+
const now = Date.now()
|
|
524
|
+
const size = batchSize > 0 ? batchSize : 1000
|
|
525
|
+
let deleted = 0
|
|
526
|
+
|
|
527
|
+
if (completedTtlMs && completedTtlMs > 0) {
|
|
528
|
+
deleted += await this._pruneStatusBatches({status: "completed", column: "completed_at_ms", cutoff: now - completedTtlMs, batchSize: size})
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (failedTtlMs && failedTtlMs > 0) {
|
|
532
|
+
deleted += await this._pruneStatusBatches({status: "failed", column: "failed_at_ms", cutoff: now - failedTtlMs, batchSize: size})
|
|
533
|
+
deleted += await this._pruneStatusBatches({status: "orphaned", column: "orphaned_at_ms", cutoff: now - failedTtlMs, batchSize: size})
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return deleted
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Deletes rows of one terminal status older than a cutoff, batch by batch,
|
|
541
|
+
* until a page returns fewer than `batchSize` rows.
|
|
542
|
+
* @param {object} args - Options.
|
|
543
|
+
* @param {string} args.status - Terminal status to prune.
|
|
544
|
+
* @param {string} args.column - Timestamp column compared against the cutoff.
|
|
545
|
+
* @param {number} args.cutoff - Delete rows whose column value is `<= cutoff`.
|
|
546
|
+
* @param {number} args.batchSize - Max rows per batch.
|
|
547
|
+
* @returns {Promise<number>} - Rows deleted for this status.
|
|
548
|
+
*/
|
|
549
|
+
async _pruneStatusBatches({status, column, cutoff, batchSize}) {
|
|
550
|
+
let deleted = 0
|
|
551
|
+
|
|
552
|
+
for (;;) {
|
|
553
|
+
const removed = await this._withDb(async (db) => {
|
|
554
|
+
const rows = await db
|
|
555
|
+
.newQuery()
|
|
556
|
+
.from(JOBS_TABLE)
|
|
557
|
+
.select("id")
|
|
558
|
+
.where({status})
|
|
559
|
+
.where(`${db.quoteColumn(column)} <= ${db.quote(cutoff)}`)
|
|
560
|
+
.limit(batchSize)
|
|
561
|
+
.results()
|
|
562
|
+
|
|
563
|
+
if (rows.length === 0) return 0
|
|
564
|
+
|
|
565
|
+
const ids = rows.map((/** @type {Record<string, ?>} */ row) => db.quote(String(row.id))).join(", ")
|
|
566
|
+
|
|
567
|
+
await db.query(`DELETE FROM ${db.quoteTable(JOBS_TABLE)} WHERE ${db.quoteColumn("id")} IN (${ids})`)
|
|
568
|
+
|
|
569
|
+
return rows.length
|
|
570
|
+
})
|
|
571
|
+
|
|
572
|
+
deleted += removed
|
|
573
|
+
if (removed < batchSize) break
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return deleted
|
|
577
|
+
}
|
|
578
|
+
|
|
493
579
|
/**
|
|
494
580
|
* Runs clear all.
|
|
495
581
|
* @returns {Promise<void>} - Resolves when cleared.
|
|
@@ -869,9 +955,10 @@ export default class BackgroundJobsStore {
|
|
|
869
955
|
* @param {import("./types.js").BackgroundJobRow} args.job - Job row.
|
|
870
956
|
* @param {?} args.error - Error.
|
|
871
957
|
* @param {boolean} args.markOrphaned - Whether marking orphaned.
|
|
958
|
+
* @param {Record<string, ?>} [args.conditions] - Update fencing conditions. Defaults to the active-handoff lease match; the time-based orphan sweep overrides this with an id/status match so it can reclaim rows whose `handoff_id` is null (e.g. handed off by an older velocious before handoff-id fencing existed).
|
|
872
959
|
* @returns {Promise<import("./types.js").BackgroundJobRow | null>} - Updated job row when the lease transition won.
|
|
873
960
|
*/
|
|
874
|
-
async _applyFailure({db, job, error, markOrphaned}) {
|
|
961
|
+
async _applyFailure({db, job, error, markOrphaned, conditions}) {
|
|
875
962
|
const now = Date.now()
|
|
876
963
|
const nextAttempt = (job.attempts || 0) + 1
|
|
877
964
|
const maxRetries = this._normalizeMaxRetries(job.maxRetries)
|
|
@@ -890,7 +977,7 @@ export default class BackgroundJobsStore {
|
|
|
890
977
|
const affectedRows = await this._updateAffectedRows(db, {
|
|
891
978
|
tableName: JOBS_TABLE,
|
|
892
979
|
data: update,
|
|
893
|
-
conditions: this._activeHandoffConditions(job)
|
|
980
|
+
conditions: conditions ?? this._activeHandoffConditions(job)
|
|
894
981
|
})
|
|
895
982
|
|
|
896
983
|
if (affectedRows !== 1) return null
|
|
@@ -178,6 +178,24 @@
|
|
|
178
178
|
* to restore the legacy fixed-interval poll.
|
|
179
179
|
* @property {number} [pollIntervalMs] - Poll interval in milliseconds. Only used
|
|
180
180
|
* when `dispatchStrategy === "polling"`. Default: `1000`.
|
|
181
|
+
* @property {BackgroundJobsRetentionConfiguration} [retention] - Retention/pruning
|
|
182
|
+
* of terminal job rows. Without pruning the jobs table grows unbounded
|
|
183
|
+
* (completed rows accumulate forever), which bloats storage and indexes and
|
|
184
|
+
* eventually slows dispatch. The main process sweeps terminal rows past their
|
|
185
|
+
* window on an interval.
|
|
186
|
+
*/
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @typedef {object} BackgroundJobsRetentionConfiguration
|
|
190
|
+
* @property {number | null} [completedTtlMs] - Delete `completed` jobs whose
|
|
191
|
+
* `completed_at_ms` is older than this many ms. `null` or `<= 0` disables
|
|
192
|
+
* completed pruning. Default: `604800000` (7 days).
|
|
193
|
+
* @property {number | null} [failedTtlMs] - Delete terminal `failed`/`orphaned`
|
|
194
|
+
* jobs older than this many ms. `null` or `<= 0` disables (keeps them for
|
|
195
|
+
* debugging). Default: `2592000000` (30 days).
|
|
196
|
+
* @property {number} [batchSize] - Rows deleted per batch. Default: `1000`.
|
|
197
|
+
* @property {number} [sweepIntervalMs] - How often the main process runs the
|
|
198
|
+
* retention sweep. Default: `3600000` (1 hour).
|
|
181
199
|
*/
|
|
182
200
|
|
|
183
201
|
/**
|
package/build/configuration.js
CHANGED
|
@@ -1292,8 +1292,23 @@ export default class VelociousConfiguration {
|
|
|
1292
1292
|
? configured.pollIntervalMs
|
|
1293
1293
|
: (typeof envPollInterval === "number" && Number.isFinite(envPollInterval) && envPollInterval >= 1 ? envPollInterval : 1000)
|
|
1294
1294
|
const queues = configured.queues && typeof configured.queues === "object" ? configured.queues : {}
|
|
1295
|
-
|
|
1296
|
-
|
|
1295
|
+
const configuredRetention = configured.retention && typeof configured.retention === "object" ? configured.retention : {}
|
|
1296
|
+
const retention = {
|
|
1297
|
+
completedTtlMs: typeof configuredRetention.completedTtlMs === "number" || configuredRetention.completedTtlMs === null
|
|
1298
|
+
? configuredRetention.completedTtlMs
|
|
1299
|
+
: 7 * 24 * 60 * 60 * 1000,
|
|
1300
|
+
failedTtlMs: typeof configuredRetention.failedTtlMs === "number" || configuredRetention.failedTtlMs === null
|
|
1301
|
+
? configuredRetention.failedTtlMs
|
|
1302
|
+
: 30 * 24 * 60 * 60 * 1000,
|
|
1303
|
+
batchSize: typeof configuredRetention.batchSize === "number" && configuredRetention.batchSize > 0
|
|
1304
|
+
? configuredRetention.batchSize
|
|
1305
|
+
: 1000,
|
|
1306
|
+
sweepIntervalMs: typeof configuredRetention.sweepIntervalMs === "number" && configuredRetention.sweepIntervalMs > 0
|
|
1307
|
+
? configuredRetention.sweepIntervalMs
|
|
1308
|
+
: 60 * 60 * 1000
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
return {host, port, databaseIdentifier, maxConcurrentForkedJobs, maxConcurrentInlineJobs, dispatchStrategy, pollIntervalMs, queues, retention}
|
|
1297
1312
|
}
|
|
1298
1313
|
|
|
1299
1314
|
/**
|
|
@@ -217,4 +217,22 @@ export default class RecordAttachmentHandle {
|
|
|
217
217
|
|
|
218
218
|
return await store.attachmentRowUrl({model: this.model, name: this.name, row})
|
|
219
219
|
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Purges every attachment under this (record, name): deletes the backing
|
|
223
|
+
* storage for each and removes the attachment rows. A no-op for unpersisted
|
|
224
|
+
* records. Only the attachments present when the purge starts are removed, so a
|
|
225
|
+
* concurrent attach for the same (record, name) is left intact. Throws (without
|
|
226
|
+
* deleting any rows) if a storage driver cannot delete its object, so a driver
|
|
227
|
+
* configured without a `delete` operation can never leak storage. Callers use
|
|
228
|
+
* this to clean up attachments before destroying the owner record.
|
|
229
|
+
* @returns {Promise<number>} - Number of attachments purged.
|
|
230
|
+
*/
|
|
231
|
+
async purgeAll() {
|
|
232
|
+
if (!this.model.isPersisted()) return 0
|
|
233
|
+
|
|
234
|
+
const store = recordAttachmentsStoreForModel(this.model)
|
|
235
|
+
|
|
236
|
+
return await store.purgeAll({model: this.model, name: this.name})
|
|
237
|
+
}
|
|
220
238
|
}
|
|
@@ -391,6 +391,51 @@ export default class RecordAttachmentsStore {
|
|
|
391
391
|
})
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Purges every attachment stored under (model, name): deletes each row's
|
|
396
|
+
* backing storage and then removes the attachment rows. Used to clean up an
|
|
397
|
+
* owner record's attachments before/when the owner is destroyed.
|
|
398
|
+
* @param {object} args - Options.
|
|
399
|
+
* @param {import("../index.js").default} args.model - Model instance.
|
|
400
|
+
* @param {string} args.name - Attachment name.
|
|
401
|
+
* @returns {Promise<number>} - Number of attachments purged.
|
|
402
|
+
*/
|
|
403
|
+
async purgeAll({model, name}) {
|
|
404
|
+
await this.ensureReady()
|
|
405
|
+
|
|
406
|
+
return await this._withDb(async (db) => {
|
|
407
|
+
const recordType = model.getModelClass().getModelName()
|
|
408
|
+
const recordId = String(model.id())
|
|
409
|
+
/** @type {Array<Record<string, ?>>} */
|
|
410
|
+
const rows = await db
|
|
411
|
+
.newQuery()
|
|
412
|
+
.from(ATTACHMENTS_TABLE)
|
|
413
|
+
.where({name, record_id: recordId, record_type: recordType})
|
|
414
|
+
.results()
|
|
415
|
+
|
|
416
|
+
// Refuse to purge when any row's driver cannot delete its backing storage:
|
|
417
|
+
// removing the row while the object stays behind would leak storage and
|
|
418
|
+
// discard the metadata needed to retry cleanup. Fail loudly instead.
|
|
419
|
+
for (const row of rows) {
|
|
420
|
+
const attachmentDriver = await this.resolveAttachmentDriver({model, name, row})
|
|
421
|
+
|
|
422
|
+
if (typeof attachmentDriver.delete !== "function") {
|
|
423
|
+
throw new Error(`Cannot purge attachment ${row.id} for ${recordType}#${recordId} (${name}): its storage driver does not support deletion.`)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
for (const row of rows) {
|
|
428
|
+
await this.deleteAttachmentRowStorage({model, name, row})
|
|
429
|
+
// Delete only the snapshotted row by id, so an attachment inserted for the
|
|
430
|
+
// same (record, name) after the snapshot is not removed with its storage
|
|
431
|
+
// still present (which would leave it as unreachable storage).
|
|
432
|
+
await db.delete({conditions: {id: row.id}, tableName: ATTACHMENTS_TABLE})
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return rows.length
|
|
436
|
+
})
|
|
437
|
+
}
|
|
438
|
+
|
|
394
439
|
/**
|
|
395
440
|
* Runs attachment driver by name.
|
|
396
441
|
* @param {string} driverName - Driver name.
|
|
@@ -400,7 +400,7 @@ export default class VeoliciousHttpServerClient {
|
|
|
400
400
|
const stats = await fs.stat(filePath)
|
|
401
401
|
contentLength = stats.size
|
|
402
402
|
} else {
|
|
403
|
-
contentLength = bodyIsString ?
|
|
403
|
+
contentLength = bodyIsString ? Buffer.byteLength(body, "utf8") : body.byteLength
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
response.setHeader("Content-Length", contentLength)
|
|
@@ -16,6 +16,7 @@ export default class BackgroundJobsMain {
|
|
|
16
16
|
port: number;
|
|
17
17
|
dispatchStrategy: import("../configuration-types.js").BackgroundJobsDispatchStrategy;
|
|
18
18
|
pollIntervalMs: number;
|
|
19
|
+
retention: import("../configuration-types.js").BackgroundJobsRetentionConfiguration;
|
|
19
20
|
store: BackgroundJobsStore;
|
|
20
21
|
logger: Logger;
|
|
21
22
|
/**
|
|
@@ -50,6 +51,10 @@ export default class BackgroundJobsMain {
|
|
|
50
51
|
* Narrows the runtime value to the documented type.
|
|
51
52
|
* @type {ReturnType<typeof setTimeout> | undefined} */
|
|
52
53
|
_orphanTimer: ReturnType<typeof setTimeout> | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Narrows the runtime value to the documented type.
|
|
56
|
+
* @type {ReturnType<typeof setTimeout> | undefined} */
|
|
57
|
+
_retentionTimer: ReturnType<typeof setTimeout> | undefined;
|
|
53
58
|
/**
|
|
54
59
|
* Narrows the runtime value to the documented type.
|
|
55
60
|
* @type {BackgroundJobsScheduler | undefined} */
|
|
@@ -460,6 +465,13 @@ export default class BackgroundJobsMain {
|
|
|
460
465
|
*/
|
|
461
466
|
_armScheduledTimer(): Promise<void>;
|
|
462
467
|
_sweepOrphans(): Promise<void>;
|
|
468
|
+
/**
|
|
469
|
+
* Deletes terminal job rows past their retention window so the jobs table
|
|
470
|
+
* does not grow unbounded. Runs on its own interval; failures are logged and
|
|
471
|
+
* retried on the next tick.
|
|
472
|
+
* @returns {Promise<void>} - Resolves after one retention sweep.
|
|
473
|
+
*/
|
|
474
|
+
_sweepRetention(): Promise<void>;
|
|
463
475
|
}
|
|
464
476
|
/**
|
|
465
477
|
* WorkerExecutionModeCapability type.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/main.js"],"names":[],"mappings":"AAyCA;IACE;;;;;;OAMG;IACH,2CAJG;QAAoD,aAAa,EAAzD,OAAO,qBAAqB,EAAE,OAAO;QACvB,IAAI;QACJ,IAAI;KAC5B,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/main.js"],"names":[],"mappings":"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"}
|