@shivaedev/jobs 0.29.2 → 0.31.0
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/CHANGELOG.md +39 -0
- package/README.md +74 -22
- package/dist/display.d.ts +14 -0
- package/dist/display.d.ts.map +1 -0
- package/dist/display.js +2 -0
- package/dist/display.js.map +1 -0
- package/dist/server/ClientDatabase.d.ts +21 -0
- package/dist/server/ClientDatabase.d.ts.map +1 -0
- package/dist/server/ClientDatabase.js +2 -0
- package/dist/server/ClientDatabase.js.map +1 -0
- package/dist/server/PgBoss.d.ts +2 -2
- package/dist/server/PgBoss.d.ts.map +1 -1
- package/dist/server/adapters/batchClockOffset.d.ts +2 -2
- package/dist/server/adapters/batchClockOffset.d.ts.map +1 -1
- package/dist/server/adapters/batchClockOffset.js +5 -4
- package/dist/server/adapters/batchClockOffset.js.map +1 -1
- package/dist/server/adapters/batchHandler.d.ts +5 -0
- package/dist/server/adapters/batchHandler.d.ts.map +1 -0
- package/dist/server/{internal → adapters}/batchHandler.js +6 -6
- package/dist/server/adapters/batchHandler.js.map +1 -0
- package/dist/server/adapters/clientCache.d.ts +10 -0
- package/dist/server/adapters/clientCache.d.ts.map +1 -0
- package/dist/server/{internal → adapters}/clientCache.js +5 -19
- package/dist/server/adapters/clientCache.js.map +1 -0
- package/dist/server/adapters/clientErrorLogger.js +1 -1
- package/dist/server/adapters/clientErrorLogger.js.map +1 -1
- package/dist/server/adapters/defaultClientFactory.d.ts +4 -0
- package/dist/server/adapters/defaultClientFactory.d.ts.map +1 -0
- package/dist/server/adapters/defaultClientFactory.js.map +1 -0
- package/dist/server/adapters/deferralCount.d.ts +3 -3
- package/dist/server/adapters/deferralCount.d.ts.map +1 -1
- package/dist/server/adapters/deferralCount.js +2 -2
- package/dist/server/adapters/deferralCount.js.map +1 -1
- package/dist/server/adapters/expireAttempt.d.ts +4 -0
- package/dist/server/adapters/expireAttempt.d.ts.map +1 -0
- package/dist/server/{internal → adapters}/expireAttempt.js +1 -1
- package/dist/server/{internal → adapters}/expireAttempt.js.map +1 -1
- package/dist/server/adapters/installSchema.d.ts +1 -1
- package/dist/server/adapters/installSchema.d.ts.map +1 -1
- package/dist/server/adapters/installSchema.js +1 -1
- package/dist/server/adapters/installSchema.js.map +1 -1
- package/dist/server/adapters/jobEngine.d.ts +5 -0
- package/dist/server/adapters/jobEngine.d.ts.map +1 -0
- package/dist/server/adapters/jobEngine.js +50 -0
- package/dist/server/adapters/jobEngine.js.map +1 -0
- package/dist/server/adapters/lifecycle.d.ts +11 -0
- package/dist/server/adapters/lifecycle.d.ts.map +1 -0
- package/dist/server/adapters/lifecycle.js +48 -0
- package/dist/server/adapters/lifecycle.js.map +1 -0
- package/dist/server/adapters/listJobs.d.ts +5 -0
- package/dist/server/adapters/listJobs.d.ts.map +1 -0
- package/dist/server/adapters/listJobs.js +37 -0
- package/dist/server/adapters/listJobs.js.map +1 -0
- package/dist/server/adapters/observeTestingSettlements.d.ts +6 -0
- package/dist/server/adapters/observeTestingSettlements.d.ts.map +1 -0
- package/dist/server/adapters/observeTestingSettlements.js.map +1 -0
- package/dist/server/adapters/queueAdministration.d.ts +4 -0
- package/dist/server/adapters/queueAdministration.d.ts.map +1 -0
- package/dist/server/adapters/queueAdministration.js +42 -0
- package/dist/server/adapters/queueAdministration.js.map +1 -0
- package/dist/server/adapters/registerWorker.d.ts +5 -3
- package/dist/server/adapters/registerWorker.d.ts.map +1 -1
- package/dist/server/adapters/registerWorker.js +16 -7
- package/dist/server/adapters/registerWorker.js.map +1 -1
- package/dist/server/adapters/retireJobs.d.ts +3 -3
- package/dist/server/adapters/retireJobs.d.ts.map +1 -1
- package/dist/server/adapters/retireJobs.js +3 -3
- package/dist/server/adapters/retireJobs.js.map +1 -1
- package/dist/server/adapters/schemaIndexBuilds.d.ts +2 -2
- package/dist/server/adapters/schemaIndexBuilds.d.ts.map +1 -1
- package/dist/server/adapters/schemaIndexBuilds.js.map +1 -1
- package/dist/server/adapters/settleDeferral.d.ts +7 -0
- package/dist/server/adapters/settleDeferral.d.ts.map +1 -0
- package/dist/server/{internal → adapters}/settleDeferral.js +2 -2
- package/dist/server/adapters/settleDeferral.js.map +1 -0
- package/dist/server/adapters/testingClient.d.ts +14 -0
- package/dist/server/adapters/testingClient.d.ts.map +1 -0
- package/dist/server/{internal → adapters}/testingClient.js +15 -11
- package/dist/server/adapters/testingClient.js.map +1 -0
- package/dist/server/adapters/testingController.d.ts +34 -0
- package/dist/server/adapters/testingController.d.ts.map +1 -0
- package/dist/server/adapters/testingController.js +98 -0
- package/dist/server/adapters/testingController.js.map +1 -0
- package/dist/server/adapters/testingDueNow.d.ts +3 -3
- package/dist/server/adapters/testingDueNow.d.ts.map +1 -1
- package/dist/server/adapters/testingDueNow.js +2 -2
- package/dist/server/adapters/testingDueNow.js.map +1 -1
- package/dist/server/adapters/testingEngine.d.ts +5 -0
- package/dist/server/adapters/testingEngine.d.ts.map +1 -0
- package/dist/server/adapters/testingEngine.js +23 -0
- package/dist/server/adapters/testingEngine.js.map +1 -0
- package/dist/server/adapters/testingExpirations.d.ts +6 -4
- package/dist/server/adapters/testingExpirations.d.ts.map +1 -1
- package/dist/server/adapters/testingExpirations.js.map +1 -1
- package/dist/server/adapters/transactionDb.d.ts +2 -2
- package/dist/server/adapters/transactionDb.d.ts.map +1 -1
- package/dist/server/adapters/transactionDb.js.map +1 -1
- package/dist/server/client.d.ts +92 -18
- package/dist/server/client.d.ts.map +1 -1
- package/dist/server/clientOptions.d.ts +75 -0
- package/dist/server/clientOptions.d.ts.map +1 -0
- package/dist/server/clientOptions.js +2 -0
- package/dist/server/clientOptions.js.map +1 -0
- package/dist/server/defineSchedule.d.ts +1 -1
- package/dist/server/defineSchedule.d.ts.map +1 -1
- package/dist/server/defineSchedule.js +2 -1
- package/dist/server/defineSchedule.js.map +1 -1
- package/dist/server/definition.d.ts +20 -18
- package/dist/server/definition.d.ts.map +1 -1
- package/dist/server/definition.js +3 -1
- package/dist/server/definition.js.map +1 -1
- package/dist/server/engineClient.d.ts +26 -0
- package/dist/server/engineClient.d.ts.map +1 -0
- package/dist/server/engineClient.js +2 -0
- package/dist/server/engineClient.js.map +1 -0
- package/dist/server/enqueueOptions.d.ts +22 -0
- package/dist/server/enqueueOptions.d.ts.map +1 -0
- package/dist/server/enqueueOptions.js +2 -0
- package/dist/server/enqueueOptions.js.map +1 -0
- package/dist/server/error.d.ts +11 -1
- package/dist/server/error.d.ts.map +1 -1
- package/dist/server/error.js +8 -1
- package/dist/server/error.js.map +1 -1
- package/dist/server/health.d.ts +2 -2
- package/dist/server/health.d.ts.map +1 -1
- package/dist/server/internal/acquireClient.d.ts +11 -0
- package/dist/server/internal/acquireClient.d.ts.map +1 -0
- package/dist/server/internal/acquireClient.js +21 -0
- package/dist/server/internal/acquireClient.js.map +1 -0
- package/dist/server/internal/adapters/testingFetches.d.ts +2 -2
- package/dist/server/internal/adapters/testingFetches.d.ts.map +1 -1
- package/dist/server/internal/adapters/testingFetches.js.map +1 -1
- package/dist/server/internal/clientErrors.d.ts +1 -1
- package/dist/server/internal/clientErrors.d.ts.map +1 -1
- package/dist/server/internal/countsFor.d.ts +3 -3
- package/dist/server/internal/countsFor.d.ts.map +1 -1
- package/dist/server/internal/countsFor.js +2 -2
- package/dist/server/internal/countsFor.js.map +1 -1
- package/dist/server/internal/deadLetterResolutionPlan.js +1 -1
- package/dist/server/internal/deadLetterResolutionPlan.js.map +1 -1
- package/dist/server/internal/deadlineOf.d.ts +2 -2
- package/dist/server/internal/deadlineOf.d.ts.map +1 -1
- package/dist/server/internal/deadlineOf.js.map +1 -1
- package/dist/server/internal/deferrableAttempt.d.ts +4 -5
- package/dist/server/internal/deferrableAttempt.d.ts.map +1 -1
- package/dist/server/internal/deferrableAttempt.js +5 -14
- package/dist/server/internal/deferrableAttempt.js.map +1 -1
- package/dist/server/internal/deferral.d.ts +3 -3
- package/dist/server/internal/deferral.d.ts.map +1 -1
- package/dist/server/internal/deferral.js.map +1 -1
- package/dist/server/internal/deferralStorePlan.js +2 -2
- package/dist/server/internal/deferralStorePlan.js.map +1 -1
- package/dist/server/internal/definitionOptions.d.ts +7 -7
- package/dist/server/internal/definitionOptions.d.ts.map +1 -1
- package/dist/server/internal/engine.d.ts +30 -0
- package/dist/server/internal/engine.d.ts.map +1 -0
- package/dist/server/internal/engine.js +6 -0
- package/dist/server/internal/engine.js.map +1 -0
- package/dist/server/internal/enqueueFor.d.ts +2 -2
- package/dist/server/internal/enqueueFor.d.ts.map +1 -1
- package/dist/server/internal/enqueueFor.js +3 -3
- package/dist/server/internal/enqueueFor.js.map +1 -1
- package/dist/server/internal/ensureQueue.d.ts +2 -3
- package/dist/server/internal/ensureQueue.d.ts.map +1 -1
- package/dist/server/internal/ensureQueue.js +2 -2
- package/dist/server/internal/ensureQueue.js.map +1 -1
- package/dist/server/internal/healthFor.d.ts +3 -3
- package/dist/server/internal/healthFor.d.ts.map +1 -1
- package/dist/server/internal/healthFor.js +2 -2
- package/dist/server/internal/healthFor.js.map +1 -1
- package/dist/server/internal/jobControl.d.ts +9 -4
- package/dist/server/internal/jobControl.d.ts.map +1 -1
- package/dist/server/internal/jobControl.js +13 -40
- package/dist/server/internal/jobControl.js.map +1 -1
- package/dist/server/internal/jobListings.d.ts +16 -0
- package/dist/server/internal/jobListings.d.ts.map +1 -0
- package/dist/server/internal/jobListings.js +52 -0
- package/dist/server/internal/jobListings.js.map +1 -0
- package/dist/server/internal/jobStorePlans.js +2 -2
- package/dist/server/internal/jobStorePlans.js.map +1 -1
- package/dist/server/internal/lifecycle.d.ts +11 -7
- package/dist/server/internal/lifecycle.d.ts.map +1 -1
- package/dist/server/internal/lifecycle.js +2 -49
- package/dist/server/internal/lifecycle.js.map +1 -1
- package/dist/server/internal/makeService.d.ts +5 -3
- package/dist/server/internal/makeService.d.ts.map +1 -1
- package/dist/server/internal/makeService.js +15 -11
- package/dist/server/internal/makeService.js.map +1 -1
- package/dist/server/internal/register.d.ts +5 -5
- package/dist/server/internal/register.d.ts.map +1 -1
- package/dist/server/internal/register.js +17 -29
- package/dist/server/internal/register.js.map +1 -1
- package/dist/server/internal/registerReleasedJobs.d.ts +4 -4
- package/dist/server/internal/registerReleasedJobs.d.ts.map +1 -1
- package/dist/server/internal/registerReleasedJobs.js +2 -2
- package/dist/server/internal/registerReleasedJobs.js.map +1 -1
- package/dist/server/internal/retryWake.d.ts +1 -1
- package/dist/server/internal/retryWake.d.ts.map +1 -1
- package/dist/server/internal/retryWake.js +2 -2
- package/dist/server/internal/retryWake.js.map +1 -1
- package/dist/server/internal/runQueueWorker.d.ts +6 -4
- package/dist/server/internal/runQueueWorker.d.ts.map +1 -1
- package/dist/server/internal/runQueueWorker.js +5 -5
- package/dist/server/internal/runQueueWorker.js.map +1 -1
- package/dist/server/internal/storedRows.d.ts +2 -2
- package/dist/server/internal/storedRows.d.ts.map +1 -1
- package/dist/server/internal/storedRows.js +2 -2
- package/dist/server/internal/storedRows.js.map +1 -1
- package/dist/server/internal/testingDeferrals.d.ts +3 -3
- package/dist/server/internal/testingDeferrals.d.ts.map +1 -1
- package/dist/server/internal/testingDeferrals.js +2 -2
- package/dist/server/internal/testingDeferrals.js.map +1 -1
- package/dist/server/internal/testingIdle.d.ts +3 -3
- package/dist/server/internal/testingIdle.d.ts.map +1 -1
- package/dist/server/internal/testingIdle.js +11 -5
- package/dist/server/internal/testingIdle.js.map +1 -1
- package/dist/server/internal/testingReadout.d.ts +7 -41
- package/dist/server/internal/testingReadout.d.ts.map +1 -1
- package/dist/server/internal/testingReadout.js +2 -2
- package/dist/server/internal/testingReadout.js.map +1 -1
- package/dist/server/internal/testingRegistrations.d.ts +4 -0
- package/dist/server/internal/testingRegistrations.d.ts.map +1 -0
- package/dist/server/internal/testingRegistrations.js +8 -0
- package/dist/server/internal/testingRegistrations.js.map +1 -0
- package/dist/server/internal/testingSchedules.d.ts +3 -3
- package/dist/server/internal/testingSchedules.d.ts.map +1 -1
- package/dist/server/internal/testingSchedules.js +1 -1
- package/dist/server/internal/testingSchedules.js.map +1 -1
- package/dist/server/internal/testingSnapshot.d.ts +4 -4
- package/dist/server/internal/testingSnapshot.d.ts.map +1 -1
- package/dist/server/internal/testingSnapshot.js +7 -4
- package/dist/server/internal/testingSnapshot.js.map +1 -1
- package/dist/server/internal/testingWaits.d.ts +8 -7
- package/dist/server/internal/testingWaits.d.ts.map +1 -1
- package/dist/server/internal/testingWaits.js +3 -3
- package/dist/server/internal/testingWaits.js.map +1 -1
- package/dist/server/internal/tracing.d.ts +2 -2
- package/dist/server/internal/tracing.d.ts.map +1 -1
- package/dist/server/internal/tracing.js.map +1 -1
- package/dist/server/internal/validateQueueNames.d.ts +2 -2
- package/dist/server/internal/validateQueueNames.d.ts.map +1 -1
- package/dist/server/internal/validateQueueNames.js +1 -1
- package/dist/server/internal/validateQueueNames.js.map +1 -1
- package/dist/server/internal/waitForBatchExpiry.d.ts +4 -4
- package/dist/server/internal/waitForBatchExpiry.d.ts.map +1 -1
- package/dist/server/internal/waitForBatchExpiry.js +1 -1
- package/dist/server/internal/waitForBatchExpiry.js.map +1 -1
- package/dist/server/job.d.ts +42 -0
- package/dist/server/job.d.ts.map +1 -0
- package/dist/server/job.js +2 -0
- package/dist/server/job.js.map +1 -0
- package/dist/server/jobModule.d.ts +9 -0
- package/dist/server/jobModule.d.ts.map +1 -0
- package/dist/server/jobModule.js +15 -0
- package/dist/server/jobModule.js.map +1 -0
- package/dist/server/queueOptions.d.ts +19 -0
- package/dist/server/queueOptions.d.ts.map +1 -0
- package/dist/server/queueOptions.js +2 -0
- package/dist/server/queueOptions.js.map +1 -0
- package/dist/server/readJobFailure.d.ts +5 -0
- package/dist/server/readJobFailure.d.ts.map +1 -0
- package/dist/server/readJobFailure.js +8 -0
- package/dist/server/readJobFailure.js.map +1 -0
- package/dist/server/schema.d.ts +1 -1
- package/dist/server/schema.d.ts.map +1 -1
- package/dist/server/schema.js +18 -7
- package/dist/server/schema.js.map +1 -1
- package/dist/server/service.d.ts +15 -10
- package/dist/server/service.d.ts.map +1 -1
- package/dist/server/service.js +26 -33
- package/dist/server/service.js.map +1 -1
- package/dist/server/testing.d.ts +32 -70
- package/dist/server/testing.d.ts.map +1 -1
- package/dist/server/testing.js +9 -96
- package/dist/server/testing.js.map +1 -1
- package/dist/server/workerOptions.d.ts +25 -0
- package/dist/server/workerOptions.d.ts.map +1 -0
- package/dist/server/workerOptions.js +2 -0
- package/dist/server/workerOptions.js.map +1 -0
- package/package.json +11 -16
- package/src/display.ts +12 -0
- package/src/server/ClientDatabase.ts +7 -0
- package/src/server/PgBoss.ts +2 -2
- package/src/server/adapters/batchClockOffset.ts +8 -5
- package/src/server/{internal → adapters}/batchHandler.ts +13 -13
- package/src/server/{internal → adapters}/clientCache.ts +10 -22
- package/src/server/adapters/clientErrorLogger.ts +1 -1
- package/src/server/{defaultClientFactory.ts → adapters/defaultClientFactory.ts} +4 -2
- package/src/server/adapters/deferralCount.ts +5 -5
- package/src/server/{internal → adapters}/expireAttempt.ts +4 -3
- package/src/server/adapters/installSchema.ts +2 -2
- package/src/server/adapters/jobEngine.ts +59 -0
- package/src/server/adapters/lifecycle.ts +67 -0
- package/src/server/adapters/listJobs.ts +43 -0
- package/src/server/{internal → adapters}/observeTestingSettlements.ts +2 -2
- package/src/server/adapters/queueAdministration.ts +47 -0
- package/src/server/adapters/registerWorker.ts +24 -13
- package/src/server/adapters/retireJobs.ts +5 -5
- package/src/server/adapters/schemaIndexBuilds.ts +2 -2
- package/src/server/{internal → adapters}/settleDeferral.ts +5 -5
- package/src/server/{internal → adapters}/testingClient.ts +34 -26
- package/src/server/adapters/testingController.ts +109 -0
- package/src/server/adapters/testingDueNow.ts +5 -5
- package/src/server/adapters/testingEngine.ts +28 -0
- package/src/server/adapters/testingExpirations.ts +6 -4
- package/src/server/adapters/transactionDb.ts +2 -2
- package/src/server/client.ts +83 -30
- package/src/server/clientOptions.ts +81 -0
- package/src/server/defineSchedule.ts +3 -2
- package/src/server/definition.ts +24 -20
- package/src/server/engineClient.ts +24 -0
- package/src/server/enqueueOptions.ts +20 -0
- package/src/server/error.ts +17 -2
- package/src/server/health.ts +2 -2
- package/src/server/internal/acquireClient.ts +26 -0
- package/src/server/internal/adapters/testingFetches.ts +2 -2
- package/src/server/internal/clientErrors.ts +1 -1
- package/src/server/internal/countsFor.ts +5 -5
- package/src/server/internal/deadLetterResolutionPlan.ts +1 -1
- package/src/server/internal/deadlineOf.ts +2 -2
- package/src/server/internal/deferrableAttempt.ts +5 -22
- package/src/server/internal/deferral.ts +3 -3
- package/src/server/internal/deferralStorePlan.ts +2 -2
- package/src/server/internal/definitionOptions.ts +1 -1
- package/src/server/internal/engine.ts +41 -0
- package/src/server/internal/enqueueFor.ts +5 -5
- package/src/server/internal/ensureQueue.ts +5 -6
- package/src/server/internal/healthFor.ts +4 -4
- package/src/server/internal/jobControl.ts +15 -50
- package/src/server/internal/jobListings.ts +85 -0
- package/src/server/internal/jobStorePlans.ts +2 -2
- package/src/server/internal/lifecycle.ts +14 -55
- package/src/server/internal/makeService.ts +21 -13
- package/src/server/internal/register.ts +27 -45
- package/src/server/internal/registerReleasedJobs.ts +6 -6
- package/src/server/internal/retryWake.ts +3 -3
- package/src/server/internal/runQueueWorker.ts +14 -8
- package/src/server/internal/storedRows.ts +3 -3
- package/src/server/internal/testingDeferrals.ts +5 -5
- package/src/server/internal/testingIdle.ts +13 -8
- package/src/server/internal/testingReadout.ts +12 -6
- package/src/server/internal/testingRegistrations.ts +10 -0
- package/src/server/internal/testingSchedules.ts +4 -4
- package/src/server/internal/testingSnapshot.ts +11 -8
- package/src/server/internal/testingWaits.ts +11 -10
- package/src/server/internal/tracing.ts +2 -2
- package/src/server/internal/validateQueueNames.ts +2 -2
- package/src/server/internal/waitForBatchExpiry.ts +4 -4
- package/src/server/job.ts +42 -0
- package/src/server/jobModule.ts +30 -0
- package/src/server/queueOptions.ts +20 -0
- package/src/server/readJobFailure.ts +12 -0
- package/src/server/schema.ts +18 -7
- package/src/server/service.ts +63 -59
- package/src/server/testing.ts +17 -108
- package/src/server/workerOptions.ts +28 -0
- package/dist/server/defaultClientFactory.d.ts +0 -3
- package/dist/server/defaultClientFactory.d.ts.map +0 -1
- package/dist/server/defaultClientFactory.js.map +0 -1
- package/dist/server/internal/batchHandler.d.ts +0 -5
- package/dist/server/internal/batchHandler.d.ts.map +0 -1
- package/dist/server/internal/batchHandler.js.map +0 -1
- package/dist/server/internal/clientCache.d.ts +0 -8
- package/dist/server/internal/clientCache.d.ts.map +0 -1
- package/dist/server/internal/clientCache.js.map +0 -1
- package/dist/server/internal/expireAttempt.d.ts +0 -3
- package/dist/server/internal/expireAttempt.d.ts.map +0 -1
- package/dist/server/internal/listJobs.d.ts +0 -5
- package/dist/server/internal/listJobs.d.ts.map +0 -1
- package/dist/server/internal/listJobs.js +0 -75
- package/dist/server/internal/listJobs.js.map +0 -1
- package/dist/server/internal/observeTestingSettlements.d.ts +0 -6
- package/dist/server/internal/observeTestingSettlements.d.ts.map +0 -1
- package/dist/server/internal/observeTestingSettlements.js.map +0 -1
- package/dist/server/internal/settleDeferral.d.ts +0 -7
- package/dist/server/internal/settleDeferral.d.ts.map +0 -1
- package/dist/server/internal/settleDeferral.js.map +0 -1
- package/dist/server/internal/testingClient.d.ts +0 -13
- package/dist/server/internal/testingClient.d.ts.map +0 -1
- package/dist/server/internal/testingClient.js.map +0 -1
- package/src/server/internal/listJobs.ts +0 -116
- /package/dist/server/{defaultClientFactory.js → adapters/defaultClientFactory.js} +0 -0
- /package/dist/server/{internal → adapters}/observeTestingSettlements.js +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Context, Effect } from "effect";
|
|
2
|
+
import type { PgBossClient } from "#server/client.ts";
|
|
3
|
+
import { toPgBossError } from "#server/error.ts";
|
|
4
|
+
import { deferralObservers } from "#server/internal/deferral.ts";
|
|
5
|
+
import type { JobEngine } from "#server/internal/engine.ts";
|
|
6
|
+
import { listJobs } from "./listJobs.ts";
|
|
7
|
+
import { queueAdministration } from "./queueAdministration.ts";
|
|
8
|
+
import { settleDeferral } from "./settleDeferral.ts";
|
|
9
|
+
|
|
10
|
+
export function jobEngine(client: PgBossClient, schema = "pgboss"): Context.Service.Shape<typeof JobEngine> {
|
|
11
|
+
return {
|
|
12
|
+
...queueAdministration(client),
|
|
13
|
+
getJob: (queue, id) =>
|
|
14
|
+
Effect.tryPromise({
|
|
15
|
+
catch: (error) => toPgBossError("getJob", error, queue),
|
|
16
|
+
try: async () => {
|
|
17
|
+
if (client.findJobs === undefined) {
|
|
18
|
+
throw new TypeError("This pg-boss client does not support findJobs");
|
|
19
|
+
}
|
|
20
|
+
return (await client.findJobs(queue, { id }))[0];
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
listJobs: listJobs(client, schema),
|
|
24
|
+
retry: (queue, id) =>
|
|
25
|
+
Effect.tryPromise({
|
|
26
|
+
catch: (error) => toPgBossError("retry", error, queue),
|
|
27
|
+
try: async () => {
|
|
28
|
+
if (client.findJobs === undefined || client.retry === undefined || client.update === undefined) {
|
|
29
|
+
throw new TypeError("This pg-boss client does not support findJobs, retry and update");
|
|
30
|
+
}
|
|
31
|
+
const job = (await client.findJobs(queue, { id }))[0];
|
|
32
|
+
if (job?.state !== "failed" && job?.state !== "retry") {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (job.state === "failed") {
|
|
36
|
+
await client.retry(queue, id);
|
|
37
|
+
}
|
|
38
|
+
await client.update(queue, undefined, { id, startAfter: 0 });
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
settleDeferral: (job, at, reason, maxDeferrals) =>
|
|
42
|
+
Effect.tryPromise({
|
|
43
|
+
catch: (error) => toPgBossError("settle", error, job.name),
|
|
44
|
+
try: () => settleDeferral(client, schema, job, at, reason, maxDeferrals),
|
|
45
|
+
}).pipe(
|
|
46
|
+
Effect.tap((settled) =>
|
|
47
|
+
Effect.sync(() => {
|
|
48
|
+
if (!settled.claimed || settled.deferred) {
|
|
49
|
+
// Native batch settlement must not match a sibling's new claim at the unchanged retry count.
|
|
50
|
+
job.retryCount = Number.MAX_SAFE_INTEGER;
|
|
51
|
+
}
|
|
52
|
+
if (settled.deferred) {
|
|
53
|
+
deferralObservers.get(client)?.();
|
|
54
|
+
}
|
|
55
|
+
}),
|
|
56
|
+
),
|
|
57
|
+
),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type Context, Effect, Exit, Scope } from "effect";
|
|
2
|
+
import type { PgBossClient } from "#server/client.ts";
|
|
3
|
+
import type { StopOptions } from "#server/clientOptions.ts";
|
|
4
|
+
import { PgBossClientOptionsError, toPgBossError } from "#server/error.ts";
|
|
5
|
+
import { observeClientErrors } from "#server/internal/clientErrors.ts";
|
|
6
|
+
import { jobStorePlans } from "#server/internal/jobStorePlans.ts";
|
|
7
|
+
import type { AcquireClientOptions, ClientEngine } from "#server/internal/lifecycle.ts";
|
|
8
|
+
import { acquireCachedClient, releaseCachedClient } from "./clientCache.ts";
|
|
9
|
+
import { defaultClientFactory } from "./defaultClientFactory.ts";
|
|
10
|
+
|
|
11
|
+
export interface StartingClient {
|
|
12
|
+
readonly client: PgBossClient;
|
|
13
|
+
readonly ready: Promise<void>;
|
|
14
|
+
readonly stop: (options?: StopOptions) => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function startClient(options: AcquireClientOptions, report: (error: Error) => void): StartingClient {
|
|
18
|
+
const client = (options.clientFactory ?? defaultClientFactory)(options.constructor);
|
|
19
|
+
observeClientErrors(client, report);
|
|
20
|
+
let closing = false;
|
|
21
|
+
const ready = (async () => {
|
|
22
|
+
await client.start();
|
|
23
|
+
if (!closing && options.constructor.migrate !== false && client.getDb !== undefined) {
|
|
24
|
+
for (const plan of jobStorePlans(options.constructor.schema ?? "pgboss")) {
|
|
25
|
+
await client.getDb().executeSql(plan);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})();
|
|
29
|
+
return {
|
|
30
|
+
client,
|
|
31
|
+
ready,
|
|
32
|
+
stop: (settings) => {
|
|
33
|
+
closing = true;
|
|
34
|
+
return client.stop(settings);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const acquire = Effect.fnUntraced(function* (options: AcquireClientOptions, report: (error: Error) => void, stop?: StopOptions) {
|
|
40
|
+
const scope = yield* Scope.fork(yield* Scope.Scope);
|
|
41
|
+
return yield* Effect.gen(function* () {
|
|
42
|
+
const lease = yield* Effect.acquireRelease(
|
|
43
|
+
Effect.try({
|
|
44
|
+
catch: (error) => (error instanceof PgBossClientOptionsError ? error : toPgBossError("start", error)),
|
|
45
|
+
try: () =>
|
|
46
|
+
options.clientCacheKey === undefined
|
|
47
|
+
? { ...startClient(options, report), reused: false }
|
|
48
|
+
: acquireCachedClient(options.clientCacheKey, options.constructor, () => startClient(options, report)),
|
|
49
|
+
}),
|
|
50
|
+
(acquired) =>
|
|
51
|
+
Effect.tryPromise({
|
|
52
|
+
catch: (error) => toPgBossError("stop", error),
|
|
53
|
+
try: async () => {
|
|
54
|
+
const client = options.clientCacheKey === undefined ? acquired : releaseCachedClient(options.clientCacheKey);
|
|
55
|
+
await client?.stop(stop);
|
|
56
|
+
},
|
|
57
|
+
}).pipe(Effect.catch(Effect.logError)),
|
|
58
|
+
);
|
|
59
|
+
yield* Effect.tryPromise({ catch: (error) => toPgBossError("start", error), try: () => lease.ready });
|
|
60
|
+
return { ...(options.clientCacheKey === undefined ? {} : { cacheKey: options.clientCacheKey }), client: lease.client, reused: lease.reused };
|
|
61
|
+
}).pipe(
|
|
62
|
+
Effect.provideService(Scope.Scope, scope),
|
|
63
|
+
Effect.onError((cause) => Scope.close(scope, Exit.failCause(cause))),
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
export const clientEngine: Context.Service.Shape<typeof ClientEngine> = { acquire };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Context, Effect } from "effect";
|
|
2
|
+
import type { PgBossClient } from "#server/client.ts";
|
|
3
|
+
import { toPgBossError } from "#server/error.ts";
|
|
4
|
+
import { deadLetterQueueName } from "#server/health.ts";
|
|
5
|
+
import type { JobEngine } from "#server/internal/engine.ts";
|
|
6
|
+
import { quotedSchema } from "#server/internal/storedRows.ts";
|
|
7
|
+
|
|
8
|
+
export function listJobs(client: PgBossClient, schema: string): Context.Service.Shape<typeof JobEngine>["listJobs"] {
|
|
9
|
+
return (listing, queue, after, limit) =>
|
|
10
|
+
Effect.tryPromise({
|
|
11
|
+
catch: (error) => toPgBossError(listing, error, queue),
|
|
12
|
+
try: async () => {
|
|
13
|
+
if (client.getDb === undefined) {
|
|
14
|
+
throw new TypeError("This pg-boss client does not support getDb");
|
|
15
|
+
}
|
|
16
|
+
const quoted = quotedSchema(schema);
|
|
17
|
+
const { rows: storedRows } = await client.getDb().executeSql(
|
|
18
|
+
`SELECT
|
|
19
|
+
id,
|
|
20
|
+
name AS queue,
|
|
21
|
+
state,
|
|
22
|
+
retry_count AS "retryCount",
|
|
23
|
+
retry_limit AS "retryLimit",
|
|
24
|
+
created_on AS "createdOn",
|
|
25
|
+
started_on AS "startedOn",
|
|
26
|
+
completed_on AS "completedOn",
|
|
27
|
+
output,
|
|
28
|
+
data,
|
|
29
|
+
source_id AS "sourceId",
|
|
30
|
+
source_output AS "sourceOutput",
|
|
31
|
+
to_json(created_on) #>> '{}' AS "cursorAt"
|
|
32
|
+
FROM ${quoted}.job
|
|
33
|
+
WHERE name = $1
|
|
34
|
+
${listing === "failedJobs" ? "AND state = 'failed'" : ""}
|
|
35
|
+
AND ($2::timestamptz IS NULL OR (created_on, id) < ($2::timestamptz, $3::uuid))
|
|
36
|
+
ORDER BY created_on DESC, id DESC
|
|
37
|
+
LIMIT $4`,
|
|
38
|
+
[listing === "failedJobs" ? queue : deadLetterQueueName(queue), after?.[0] ?? null, after?.[1] ?? null, limit + 1],
|
|
39
|
+
);
|
|
40
|
+
return storedRows;
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EngineClient } from "#server/engineClient.ts";
|
|
2
2
|
|
|
3
|
-
export function observeTestingSettlements(client:
|
|
3
|
+
export function observeTestingSettlements(client: EngineClient, settled: (name: string) => void) {
|
|
4
4
|
const observers = new Map<string, () => void>();
|
|
5
5
|
return {
|
|
6
6
|
close() {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { PgBossClient } from "#server/client.ts";
|
|
3
|
+
import { PgBossNotFoundError, toPgBossError } from "#server/error.ts";
|
|
4
|
+
import type { PgBossService } from "#server/service.ts";
|
|
5
|
+
|
|
6
|
+
export function queueAdministration(client: PgBossClient): Pick<PgBossService, "deleteQueue" | "refreshQueueStats" | "unschedule"> {
|
|
7
|
+
return {
|
|
8
|
+
deleteQueue: (queue) =>
|
|
9
|
+
Effect.tryPromise({
|
|
10
|
+
catch: (error) => (error instanceof PgBossNotFoundError ? error : toPgBossError("deleteQueue", error, queue.name)),
|
|
11
|
+
try: async () => {
|
|
12
|
+
if (client.deleteQueue === undefined) {
|
|
13
|
+
throw new TypeError("This pg-boss client does not support deleteQueue");
|
|
14
|
+
}
|
|
15
|
+
if ((await client.getQueue(queue.name)) === null) {
|
|
16
|
+
throw new PgBossNotFoundError({ operation: "deleteQueue", queue: queue.name });
|
|
17
|
+
}
|
|
18
|
+
await client.offWork(queue.name);
|
|
19
|
+
await client.deleteQueue(queue.name);
|
|
20
|
+
},
|
|
21
|
+
}),
|
|
22
|
+
refreshQueueStats: (queue) =>
|
|
23
|
+
Effect.tryPromise({
|
|
24
|
+
catch: (error) => toPgBossError("refreshQueueStats", error, queue?.name),
|
|
25
|
+
try: async () => {
|
|
26
|
+
if (client.supervise === undefined) {
|
|
27
|
+
throw new TypeError("This pg-boss client does not support supervise");
|
|
28
|
+
}
|
|
29
|
+
await client.supervise(queue?.name);
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
unschedule: (schedule) =>
|
|
33
|
+
Effect.tryPromise({
|
|
34
|
+
catch: (error) => (error instanceof PgBossNotFoundError ? error : toPgBossError("unschedule", error, schedule.name)),
|
|
35
|
+
try: async () => {
|
|
36
|
+
if (client.getSchedule === undefined || client.unschedule === undefined) {
|
|
37
|
+
throw new TypeError("This pg-boss client does not support getSchedule and unschedule");
|
|
38
|
+
}
|
|
39
|
+
const key = schedule.scheduleOptions.key ?? "";
|
|
40
|
+
if ((await client.getSchedule(schedule.name, key)) === null) {
|
|
41
|
+
throw new PgBossNotFoundError({ key, operation: "unschedule", queue: schedule.name });
|
|
42
|
+
}
|
|
43
|
+
await client.unschedule(schedule.name, key);
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { Effect, Fiber, Scope } from "effect";
|
|
2
|
-
import
|
|
3
|
-
import type { PgBossClient } from "#client.ts";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { deferrableAttempt } from "#internal/deferrableAttempt.ts";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
2
|
+
import { batchHandler } from "#server/adapters/batchHandler.ts";
|
|
3
|
+
import type { PgBossClient } from "#server/client.ts";
|
|
4
|
+
import type { StopOptions } from "#server/clientOptions.ts";
|
|
5
|
+
import { toPgBossError } from "#server/error.ts";
|
|
6
|
+
import { deferrableAttempt } from "#server/internal/deferrableAttempt.ts";
|
|
7
|
+
import { JobEngine } from "#server/internal/engine.ts";
|
|
8
|
+
import { retryWake } from "#server/internal/retryWake.ts";
|
|
9
|
+
import { traceJob } from "#server/internal/tracing.ts";
|
|
10
|
+
import type { WorkerJob } from "#server/job.ts";
|
|
11
|
+
import type { WorkerOptions } from "#server/workerOptions.ts";
|
|
12
|
+
import { jobEngine } from "./jobEngine.ts";
|
|
9
13
|
|
|
10
14
|
export const registerWorker = Effect.fnUntraced(function* <R>(
|
|
11
15
|
client: PgBossClient,
|
|
12
16
|
name: string,
|
|
13
|
-
options:
|
|
14
|
-
run: (job:
|
|
17
|
+
options: WorkerOptions,
|
|
18
|
+
run: (job: WorkerJob<unknown>, deadline: number) => Effect.Effect<unknown, unknown, R>,
|
|
15
19
|
scope: Scope.Scope,
|
|
16
20
|
schema: string,
|
|
17
21
|
maxDeferrals: number,
|
|
@@ -20,15 +24,22 @@ export const registerWorker = Effect.fnUntraced(function* <R>(
|
|
|
20
24
|
const execute = Effect.runPromiseWith(yield* Effect.context<R>());
|
|
21
25
|
const workerScope = yield* Scope.fork(scope);
|
|
22
26
|
const fibers = yield* Scope.fork(workerScope);
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
const engine = jobEngine(client, schema);
|
|
28
|
+
function runJob(job: WorkerJob<unknown>, deadline: number) {
|
|
29
|
+
return traceJob(deferrableAttempt(run(job, deadline), job, maxDeferrals).pipe(Effect.provideService(JobEngine, engine)), job, name);
|
|
25
30
|
}
|
|
26
|
-
function afterRetry(attempt:
|
|
31
|
+
function afterRetry(attempt: WorkerJob<unknown>) {
|
|
27
32
|
return retryWake(client, schema, name, attempt.id, registration).pipe(Effect.forkIn(fibers), Effect.asVoid);
|
|
28
33
|
}
|
|
29
34
|
const handle = yield* Effect.try({
|
|
30
35
|
catch: (error) => toPgBossError("register", error, name),
|
|
31
|
-
try: () =>
|
|
36
|
+
try: () => {
|
|
37
|
+
const unsupported = (["perJobResults", "transactional"] as const).filter((mode) => options[mode] === true);
|
|
38
|
+
if (unsupported.length > 0) {
|
|
39
|
+
throw new TypeError(`Unsupported worker callback modes: ${unsupported.join(", ")}. Omit these options or set them to false.`);
|
|
40
|
+
}
|
|
41
|
+
return batchHandler(client, name, schema, runJob, afterRetry);
|
|
42
|
+
},
|
|
32
43
|
});
|
|
33
44
|
const registration: Promise<string> = yield* Effect.try({
|
|
34
45
|
catch: (error) => toPgBossError("register", error, name),
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import type { PgBossClient } from "#client.ts";
|
|
3
|
-
import type { JobRegistration } from "#definition.ts";
|
|
4
|
-
import { toPgBossError } from "#error.ts";
|
|
5
|
-
import { deadLetterQueueName } from "#health.ts";
|
|
6
|
-
import { registrationNames } from "#internal/register.ts";
|
|
2
|
+
import type { PgBossClient } from "#server/client.ts";
|
|
3
|
+
import type { JobRegistration } from "#server/definition.ts";
|
|
4
|
+
import { toPgBossError } from "#server/error.ts";
|
|
5
|
+
import { deadLetterQueueName } from "#server/health.ts";
|
|
6
|
+
import { registrationNames } from "#server/internal/register.ts";
|
|
7
7
|
|
|
8
8
|
export const retireJobs = Effect.fnUntraced(function* (client: PgBossClient, jobs: readonly JobRegistration[], retired: readonly string[]) {
|
|
9
9
|
yield* Effect.try({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
|
-
import type {
|
|
2
|
+
import type { EngineClient } from "#server/engineClient.ts";
|
|
3
3
|
|
|
4
4
|
const IndexBuilds = Schema.Array(Schema.Struct({ command: Schema.String, id: Schema.String }));
|
|
5
5
|
const IndexValidity = Schema.Array(Schema.Struct({ invalid: Schema.Boolean }));
|
|
@@ -28,7 +28,7 @@ export function pendingIndexInserts(schema: string, version: number, commands: r
|
|
|
28
28
|
.join("\n");
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export async function resumeSchemaIndexes(client:
|
|
31
|
+
export async function resumeSchemaIndexes(client: EngineClient, schema: string): Promise<void> {
|
|
32
32
|
const db = client.getDb();
|
|
33
33
|
const { rows } = await db.executeSql(
|
|
34
34
|
`SELECT id, command FROM ${schema}.bam WHERE starts_with(name, $1) AND status <> 'completed' ORDER BY created_on, name`,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
|
-
import type {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { quotedSchema } from "#internal/storedRows.ts";
|
|
2
|
+
import type { PgBossClient } from "#server/client.ts";
|
|
3
|
+
import { STATELY_DEFERRAL_POLICY } from "#server/internal/deferral.ts";
|
|
4
|
+
import type { JobAttempt } from "#server/internal/engine.ts";
|
|
5
|
+
import { quotedSchema } from "#server/internal/storedRows.ts";
|
|
6
6
|
|
|
7
7
|
const Settlement = Schema.Struct({ claimed: Schema.Boolean, deferred: Schema.Boolean });
|
|
8
8
|
|
|
9
|
-
export async function settleDeferral(client: PgBossClient, schema: string, job:
|
|
9
|
+
export async function settleDeferral(client: PgBossClient, schema: string, job: JobAttempt, at: string, reason: string, maxDeferrals: number) {
|
|
10
10
|
if (client.getDb === undefined) {
|
|
11
11
|
throw new TypeError("This pg-boss client does not support getDb for RetryAt");
|
|
12
12
|
}
|
|
@@ -1,33 +1,39 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { testingEngine } from "#server/adapters/testingEngine.ts";
|
|
3
|
+
import { testingExpirations } from "#server/adapters/testingExpirations.ts";
|
|
4
|
+
import type { ClientUpdateOptions, ClientUpdateRequest, ClientUpdateResult, PgBossClient } from "#server/client.ts";
|
|
5
|
+
import type { EngineClient } from "#server/engineClient.ts";
|
|
6
|
+
import type { EnqueueOptions } from "#server/enqueueOptions.ts";
|
|
7
|
+
import { deferralObservers } from "#server/internal/deferral.ts";
|
|
8
|
+
import type { TestingBatch, TestingWork } from "#server/internal/testingIdle.ts";
|
|
9
|
+
import { testingRegistration } from "#server/internal/testingRegistrations.ts";
|
|
10
|
+
import type { WorkerJob } from "#server/job.ts";
|
|
8
11
|
|
|
9
12
|
export interface TestingControls {
|
|
10
13
|
readonly changed: () => void;
|
|
11
14
|
readonly expirations: Map<string, number>;
|
|
12
15
|
readonly expirySeconds: Map<string, number>;
|
|
13
16
|
readonly failures: Set<string>;
|
|
14
|
-
readonly retryPolicy: Pick<
|
|
17
|
+
readonly retryPolicy: Pick<EnqueueOptions, "retryBackoff" | "retryDelay">;
|
|
15
18
|
readonly work: TestingWork;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
export function testingClient(client:
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
export function testingClient(client: EngineClient, schema: string, controls: TestingControls): PgBossClient {
|
|
22
|
+
const engine = testingEngine(client, schema);
|
|
23
|
+
const settlements = Effect.runSync(
|
|
24
|
+
engine.observeSettlements((name) => {
|
|
25
|
+
for (const batch of controls.work.batches) {
|
|
26
|
+
if (batch.name === name) {
|
|
27
|
+
batch.settled = true;
|
|
28
|
+
}
|
|
23
29
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
controls.changed();
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
27
33
|
const expirations = testingExpirations(client);
|
|
28
|
-
function update(request:
|
|
29
|
-
function update(name: string, data: object | null | undefined, options?:
|
|
30
|
-
async function update(request: string |
|
|
34
|
+
function update(request: ClientUpdateRequest): Promise<ClientUpdateResult>;
|
|
35
|
+
function update(name: string, data: object | null | undefined, options?: ClientUpdateOptions): Promise<ClientUpdateResult>;
|
|
36
|
+
async function update(request: string | ClientUpdateRequest, data?: object | null, options?: ClientUpdateOptions): Promise<ClientUpdateResult> {
|
|
31
37
|
const result = typeof request === "string" ? await client.update(request, data, options) : await client.update(request);
|
|
32
38
|
controls.changed();
|
|
33
39
|
return result;
|
|
@@ -82,7 +88,7 @@ export function testingClient(client: PgBoss, schema: string, controls: TestingC
|
|
|
82
88
|
try {
|
|
83
89
|
await client.stop(options);
|
|
84
90
|
} finally {
|
|
85
|
-
settlements.close
|
|
91
|
+
await Effect.runPromise(settlements.close);
|
|
86
92
|
}
|
|
87
93
|
},
|
|
88
94
|
update,
|
|
@@ -92,19 +98,18 @@ export function testingClient(client: PgBoss, schema: string, controls: TestingC
|
|
|
92
98
|
...controls.retryPolicy,
|
|
93
99
|
...(controls.retryPolicy.retryBackoff === false ? { retryDelayMax: null } : {}),
|
|
94
100
|
}),
|
|
95
|
-
work: (name, options, handler) => {
|
|
96
|
-
settlements.watch(name);
|
|
97
|
-
|
|
98
|
-
return client.work(name, options, async (jobs: Job<unknown>[]) => {
|
|
101
|
+
work: async (name, options, handler) => {
|
|
102
|
+
await Effect.runPromise(settlements.watch(name));
|
|
103
|
+
return client.work(name, options, async (jobs: readonly WorkerJob<unknown>[]) => {
|
|
99
104
|
const batch: TestingBatch = { name, settled: false };
|
|
100
105
|
controls.work.batches.add(batch);
|
|
101
106
|
controls.changed();
|
|
102
107
|
try {
|
|
103
|
-
const ready:
|
|
108
|
+
const ready: WorkerJob<unknown>[] = [];
|
|
104
109
|
for (const job of jobs) {
|
|
105
110
|
const attempts = await expirations.attemptsFor(job);
|
|
106
111
|
if (attempts !== undefined && job.retryCount + 1 >= attempts) {
|
|
107
|
-
await expireAttempt(
|
|
112
|
+
await Effect.runPromise(engine.expireAttempt(job));
|
|
108
113
|
} else {
|
|
109
114
|
ready.push(job);
|
|
110
115
|
}
|
|
@@ -121,6 +126,9 @@ export function testingClient(client: PgBoss, schema: string, controls: TestingC
|
|
|
121
126
|
};
|
|
122
127
|
const wrapped = new Proxy(client, {
|
|
123
128
|
get(target, property) {
|
|
129
|
+
if (property === testingRegistration) {
|
|
130
|
+
return (name: string) => controls.work.queues.add(name);
|
|
131
|
+
}
|
|
124
132
|
if (Object.hasOwn(overrides, property)) {
|
|
125
133
|
const override: unknown = Reflect.get(overrides, property);
|
|
126
134
|
return override;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Duration, Effect, Redacted } from "effect";
|
|
2
|
+
import { defaultClientFactory } from "#server/adapters/defaultClientFactory.ts";
|
|
3
|
+
import { testingClient } from "#server/adapters/testingClient.ts";
|
|
4
|
+
import { testingDueNow } from "#server/adapters/testingDueNow.ts";
|
|
5
|
+
import type { PgBossClientFactory } from "#server/client.ts";
|
|
6
|
+
import type { QueueDefinition } from "#server/definition.ts";
|
|
7
|
+
import type { EngineClient } from "#server/engineClient.ts";
|
|
8
|
+
import { PgBossTestingError } from "#server/error.ts";
|
|
9
|
+
import { type TestingFetches, testingFetches } from "#server/internal/adapters/testingFetches.ts";
|
|
10
|
+
import { testingDeferrals } from "#server/internal/testingDeferrals.ts";
|
|
11
|
+
import { type TestingWork, testingIdle } from "#server/internal/testingIdle.ts";
|
|
12
|
+
import { testingOperation, testingReadout } from "#server/internal/testingReadout.ts";
|
|
13
|
+
import { testingSchedules } from "#server/internal/testingSchedules.ts";
|
|
14
|
+
import { testingSignal } from "#server/internal/testingSignal.ts";
|
|
15
|
+
import { testingSnapshot } from "#server/internal/testingSnapshot.ts";
|
|
16
|
+
import { testingWaits } from "#server/internal/testingWaits.ts";
|
|
17
|
+
import type { PgBossTestingOptions } from "#server/testing.ts";
|
|
18
|
+
export function testingController(options: PgBossTestingOptions = {}) {
|
|
19
|
+
let current: EngineClient | undefined;
|
|
20
|
+
let fetches: TestingFetches | undefined;
|
|
21
|
+
let schema = "pgboss";
|
|
22
|
+
const signal = testingSignal();
|
|
23
|
+
const work: TestingWork = { batches: new Set(), queues: new Set() };
|
|
24
|
+
const controls = {
|
|
25
|
+
changed: signal.changed,
|
|
26
|
+
expirations: new Map<string, number>(),
|
|
27
|
+
expirySeconds: new Map<string, number>(),
|
|
28
|
+
failures: new Set<string>(),
|
|
29
|
+
retryPolicy: options.retryPolicy ?? {},
|
|
30
|
+
work,
|
|
31
|
+
};
|
|
32
|
+
function client(): EngineClient {
|
|
33
|
+
if (current === undefined) {
|
|
34
|
+
throw new Error("Provide the testing clientFactory to a jobs Layer first");
|
|
35
|
+
}
|
|
36
|
+
return current;
|
|
37
|
+
}
|
|
38
|
+
const clientFactory: PgBossClientFactory = (clientOptions) => {
|
|
39
|
+
if (current !== undefined) {
|
|
40
|
+
throw new Error("A testing controller owns one jobs client; create a controller per runtime");
|
|
41
|
+
}
|
|
42
|
+
schema = clientOptions.schema ?? "pgboss";
|
|
43
|
+
current = defaultClientFactory({ ...clientOptions, "__test__enableSpies": true });
|
|
44
|
+
fetches = testingFetches(current.getDb(), signal.changed);
|
|
45
|
+
return testingClient(current, clientOptions.schema ?? "pgboss", controls);
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
...testingDeferrals(client),
|
|
49
|
+
...testingDueNow(client, () => schema, signal.changed),
|
|
50
|
+
...testingSchedules(client, signal.changed),
|
|
51
|
+
...testingWaits(client, () => fetches, signal),
|
|
52
|
+
checkBatchWindow: (queue: Pick<QueueDefinition, "name" | "workerOptions">, millisecondsPerJob: number) =>
|
|
53
|
+
testingOperation(
|
|
54
|
+
"checkBatchWindow",
|
|
55
|
+
async () => {
|
|
56
|
+
const stored = await client().getQueue(queue.name);
|
|
57
|
+
if (stored === null) {
|
|
58
|
+
throw new Error("Queue is not registered");
|
|
59
|
+
}
|
|
60
|
+
if (stored.expireInSeconds === undefined) {
|
|
61
|
+
throw new Error("Queue has no expiry window");
|
|
62
|
+
}
|
|
63
|
+
if (!Number.isFinite(millisecondsPerJob) || millisecondsPerJob < 0) {
|
|
64
|
+
throw new RangeError("Job duration must be nonnegative and finite");
|
|
65
|
+
}
|
|
66
|
+
const required = (queue.workerOptions.batchSize ?? 10) * millisecondsPerJob;
|
|
67
|
+
if (required >= stored.expireInSeconds * 1000) {
|
|
68
|
+
throw new Error("Sequential batch work must fit within the queue expiry window");
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
queue.name,
|
|
72
|
+
),
|
|
73
|
+
checkSchemaVersion: (expected: number) =>
|
|
74
|
+
testingOperation("checkSchemaVersion", async () => {
|
|
75
|
+
if ((await client().schemaVersion()) !== expected) {
|
|
76
|
+
throw new Error(`Expected pg-boss schema version ${expected}`);
|
|
77
|
+
}
|
|
78
|
+
}),
|
|
79
|
+
clientFactory,
|
|
80
|
+
expireAfter: (queue: Pick<QueueDefinition, "name">, attempts: number) =>
|
|
81
|
+
Effect.try({
|
|
82
|
+
catch: (original) => new PgBossTestingError({ operation: "expireAfter", original: Redacted.make(original), queue: queue.name }),
|
|
83
|
+
try: () => {
|
|
84
|
+
if (!Number.isSafeInteger(attempts) || attempts < 1) {
|
|
85
|
+
throw new RangeError("Attempts must be a positive integer");
|
|
86
|
+
}
|
|
87
|
+
controls.expirations.set(queue.name, attempts);
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
expireIn: (queue: Pick<QueueDefinition, "name">, duration: Duration.Input) =>
|
|
91
|
+
Effect.try({
|
|
92
|
+
catch: (original) => new PgBossTestingError({ operation: "expireIn", original: Redacted.make(original), queue: queue.name }),
|
|
93
|
+
try: () => {
|
|
94
|
+
const milliseconds = Duration.toMillis(Duration.fromInputUnsafe(duration));
|
|
95
|
+
if (!Number.isFinite(milliseconds) || milliseconds < 1000) {
|
|
96
|
+
throw new RangeError("Expiry must be finite and at least one second");
|
|
97
|
+
}
|
|
98
|
+
controls.expirySeconds.set(queue.name, Math.ceil(milliseconds / 1000));
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
failNextSend: (queue: Pick<QueueDefinition, "name">) => Effect.sync(() => controls.failures.add(queue.name)).pipe(Effect.asVoid),
|
|
102
|
+
jobs: testingReadout(client),
|
|
103
|
+
revision: signal.revision,
|
|
104
|
+
schemaVersion: testingOperation("schemaVersion", () => client().schemaVersion()),
|
|
105
|
+
snapshot: testingSnapshot(client, () => schema),
|
|
106
|
+
waitForChange: signal.waitForChange,
|
|
107
|
+
waitForIdle: testingIdle(client, () => schema, work, signal),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import { quotedSchema } from "#internal/storedRows.ts";
|
|
4
|
-
import { testingOperation } from "#internal/testingReadout.ts";
|
|
1
|
+
import type { QueueDefinition } from "#server/definition.ts";
|
|
2
|
+
import type { EngineClient } from "#server/engineClient.ts";
|
|
3
|
+
import { quotedSchema } from "#server/internal/storedRows.ts";
|
|
4
|
+
import { testingOperation } from "#server/internal/testingReadout.ts";
|
|
5
5
|
|
|
6
|
-
export function testingDueNow(client: () =>
|
|
6
|
+
export function testingDueNow(client: () => EngineClient, schema: () => string, changed: () => void) {
|
|
7
7
|
function makeDue(queue: Pick<QueueDefinition, "name">, deferredOnly: boolean) {
|
|
8
8
|
return testingOperation(
|
|
9
9
|
deferredOnly ? "runDeferredNow" : "runDueNow",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Context, Effect, Redacted } from "effect";
|
|
2
|
+
import type { EngineClient } from "#server/engineClient.ts";
|
|
3
|
+
import { PgBossTestingError } from "#server/error.ts";
|
|
4
|
+
import type { TestingEngine } from "#server/internal/engine.ts";
|
|
5
|
+
import { expireAttempt } from "./expireAttempt.ts";
|
|
6
|
+
import { observeTestingSettlements } from "./observeTestingSettlements.ts";
|
|
7
|
+
|
|
8
|
+
export function testingEngine(client: EngineClient, schema: string): Context.Service.Shape<typeof TestingEngine> {
|
|
9
|
+
return {
|
|
10
|
+
expireAttempt: (job, cause) =>
|
|
11
|
+
Effect.tryPromise({
|
|
12
|
+
catch: (original) => new PgBossTestingError({ operation: "expireAttempt", original: Redacted.make(original), queue: job.name }),
|
|
13
|
+
try: () => expireAttempt(client, schema, job, cause),
|
|
14
|
+
}),
|
|
15
|
+
observeSettlements: (settled) =>
|
|
16
|
+
Effect.sync(() => {
|
|
17
|
+
const observer = observeTestingSettlements(client, settled);
|
|
18
|
+
return {
|
|
19
|
+
close: Effect.sync(() => observer.close()),
|
|
20
|
+
watch: (name: string) =>
|
|
21
|
+
Effect.try({
|
|
22
|
+
catch: (original) => new PgBossTestingError({ operation: "observeSettlements", original: Redacted.make(original), queue: name }),
|
|
23
|
+
try: () => observer.watch(name),
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EngineClient } from "#server/engineClient.ts";
|
|
2
|
+
import type { EnqueueOptions } from "#server/enqueueOptions.ts";
|
|
3
|
+
import type { WorkerJob } from "#server/job.ts";
|
|
2
4
|
|
|
3
|
-
export function testingExpirations(client:
|
|
5
|
+
export function testingExpirations(client: EngineClient) {
|
|
4
6
|
const snapshots = new Map<string, Promise<number | undefined>>();
|
|
5
7
|
return {
|
|
6
|
-
attemptsFor: (job:
|
|
7
|
-
send(name: string, data: object | null | undefined, options:
|
|
8
|
+
attemptsFor: (job: WorkerJob<unknown>) => snapshots.get(`${job.name}:${job.id}`),
|
|
9
|
+
send(name: string, data: object | null | undefined, options: EnqueueOptions, attempts: number | undefined) {
|
|
8
10
|
if (attempts === undefined && options.id === undefined) {
|
|
9
11
|
return client.send(name, data, options);
|
|
10
12
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Effect, Option } from "effect";
|
|
2
2
|
import { SqlClient } from "effect/sql";
|
|
3
|
-
import type {
|
|
3
|
+
import type { EnqueueOptions } from "#server/enqueueOptions.ts";
|
|
4
4
|
|
|
5
|
-
export const transactionDb: Effect.Effect<
|
|
5
|
+
export const transactionDb: Effect.Effect<EnqueueOptions["db"]> = Effect.gen(function* () {
|
|
6
6
|
const sql = yield* Effect.serviceOption(SqlClient.SqlClient);
|
|
7
7
|
if (Option.isNone(sql)) {
|
|
8
8
|
return undefined;
|