diff --git a/Hummingbird.docc/HummingbirdValkey/HummingbirdValkey.md b/Hummingbird.docc/HummingbirdValkey/HummingbirdValkey.md index e4e0b67b69..aade1c480d 100644 --- a/Hummingbird.docc/HummingbirdValkey/HummingbirdValkey.md +++ b/Hummingbird.docc/HummingbirdValkey/HummingbirdValkey.md @@ -42,4 +42,4 @@ try await app.runService() ## See Also -- ``JobsRedis`` \ No newline at end of file +- ``JobsValkey`` \ No newline at end of file diff --git a/Hummingbird.docc/Jobs/JobQueueDriver.md b/Hummingbird.docc/Jobs/JobQueueDriver.md index d6ab96e45c..90f3d581be 100644 --- a/Hummingbird.docc/Jobs/JobQueueDriver.md +++ b/Hummingbird.docc/Jobs/JobQueueDriver.md @@ -32,5 +32,5 @@ Defines the requirements for job queue implementation. ### Implementations - ``memory`` -- ``redis(_:configuration:logger:)`` +- ``valkey(_:configuration:logger:)`` - ``postgres(client:migrations:configuration:logger:)`` diff --git a/Hummingbird.docc/Jobs/Jobs.md b/Hummingbird.docc/Jobs/Jobs.md index 48ad4fb808..bd54c1d820 100644 --- a/Hummingbird.docc/Jobs/Jobs.md +++ b/Hummingbird.docc/Jobs/Jobs.md @@ -73,5 +73,5 @@ A Job consists of a payload and an execute method to run the job. `Jobs` provide ## See Also -- ``JobsRedis`` +- ``JobsValkey`` - ``JobsPostgres`` diff --git a/Hummingbird.docc/Jobs/JobsGuide.md b/Hummingbird.docc/Jobs/JobsGuide.md index cd7165b837..fb604906cb 100644 --- a/Hummingbird.docc/Jobs/JobsGuide.md +++ b/Hummingbird.docc/Jobs/JobsGuide.md @@ -8,7 +8,7 @@ Offload work your server would be doing to another server. ## Overview -A Job consists of a payload and an execute method to run the job. Swift Jobs provides a framework for pushing jobs onto a queue and processing them at a later point. If the driver backing up the job queue uses persistent storage then a separate server can be used to process the jobs. The module comes with a driver that stores jobs in local memory and uses your current server to process the jobs, but there are also implementations in ``JobsRedis`` and ``JobsPostgres`` that implement the job queue using a Redis database or Postgres database. +A Job consists of a payload and an execute method to run the job. Swift Jobs provides a framework for pushing jobs onto a queue and processing them at a later point. If the driver backing up the job queue uses persistent storage then a separate server can be used to process the jobs. The module comes with a driver that stores jobs in local memory and uses your current server to process the jobs, but there are also implementations in ``JobsValkey`` and ``JobsPostgres`` that implement the job queue using a Valkey/Redis database or Postgres database. ### Setting up a Job queue @@ -84,7 +84,7 @@ Or it can be added to the array of services that `Application` manages let app = Application(...) app.addServices(jobProcessor) ``` -If you want to process jobs on a separate server you will need to use a job queue driver that saves to some external storage eg ``JobsRedis/RedisJobQueue`` or ``JobsPostgres/PostgresJobQueue``. +If you want to process jobs on a separate server you will need to use a job queue driver that saves to some external storage eg ``JobsValkey/ValkeyJobQueue`` or ``JobsPostgres/PostgresJobQueue``. ## Job Scheduler diff --git a/Hummingbird.docc/JobsPostgres/JobsPostgres.md b/Hummingbird.docc/JobsPostgres/JobsPostgres.md index 2c258eef3b..3fd9ebf45d 100644 --- a/Hummingbird.docc/JobsPostgres/JobsPostgres.md +++ b/Hummingbird.docc/JobsPostgres/JobsPostgres.md @@ -166,6 +166,6 @@ You can find out more about the Job scheduler in the Jobs guide - -## Topics - -### Job Queue - -- ``RedisJobQueue`` - -## See Also - -- ``Jobs`` -- ``JobsPostgres`` -- ``Hummingbird`` diff --git a/Hummingbird.docc/JobsValkey/JobsValkey.md b/Hummingbird.docc/JobsValkey/JobsValkey.md new file mode 100644 index 0000000000..22dd919c28 --- /dev/null +++ b/Hummingbird.docc/JobsValkey/JobsValkey.md @@ -0,0 +1,139 @@ +# ``JobsValkey`` + +@Metadata { + @PageImage(purpose: icon, source: "logo") +} + +Valkey/Redis implementation for Hummingbird jobs framework + +## Overview + +Hummingbird Jobs Queue driver using [valkey-swift](https://github.com/valkey-io/valkey-swift). + +## Setup + +The Postgres job queue driver uses `ValkeyClient` from `valkey-swift`. + +The Valkey job queue configuration includes three values. +- `pollTime`: This is the amount of time between the last time the queue was empty and the next time the driver starts looking for pending jobs. +- `queueName`: Name of queue used to differentiate itself from other queues. +- `retentionPolicy`: Policy on what jobs are retained. + +```swift +import JobsValkey +import ServiceLifecycle +import Valkey + +let valkeyClient = ValkeyClient(...) +let jobQueue = JobQueue( + .valkey( + valkeyClient, + configuration: .init( + queueName: "MyJobQueue", + pollTime: .milliseconds(50) + ) + ), + logger: logger +) +``` + +## Additional Features + +There are features specific to the Valkey/Redis Job Queue implementation. + +### Push Options + +When pushing a job to the queue there are a number of options you can provide. + +#### Delaying jobs + +As with all queue drivers you can add a delay before a job is processed. The job will sit in the pending queue and will not be available for processing until time has passed its delay until time. + +```swift +// Add TestJob to the queue, but don't process it for 2 minutes +try await jobQueue.push(TestJob(), options: .init(delayUntil: .now + 120)) +``` + +### Cancellation + +The ``JobsValkey/ValkeyJobQueue`` conforms to protocol ``Jobs/CancellableJobQueue``. This requires support for cancelling jobs that are in the pending queue. It adds one new function ``JobsValkey/ValkeyJobQueue/cancel(jobID:)``. If you supply this function with the `JobID` returned by ``JobsValkey/ValkeyJobQueue/push(_:options:)`` it will remove it from the pending queue. + +```swift +// Add TestJob to the queue and immediately cancel it +let jobID = try await jobQueue.push(TestJob(), options: .init(delayUntil: .now + 120)) +try await jobQueue.cancel(jobID: jobID) +``` + +### Pause and Resume + +The ``JobsValkey/ValkeyJobQueue`` conforms to protocol ``Jobs/ResumableJobQueue``. This requires support for pausing and resuming jobs that are in the pending queue. It adds two new functions ``JobsValkey/ValkeyJobQueue/pause(jobID:)`` and ``JobsValkey/ValkeyJobQueue/resume(jobID:)``. If you supply these function with the `JobID` returned by ``JobsValkey/ValkeyJobQueue/push(_:options:)`` you can remove from the pending queue and add them back in at a later date. + +```swift +// Add TestJob to the queue and immediately remove it and then add it back to the queue +let jobID = try await jobQueue.push(TestJob(), options: .init(delayUntil: .now + 120)) +try await jobQueue.pause(jobID: jobID) +try await jobQueue.resume(jobID: jobID) +``` + +### Job retention + +The queue has options to retain jobs once it has finished with them depending on status. By default the queue will retain failed jobs and drop cancelled or completed jobs, but these decisions are configurable. + +```swift +let jobQueue = JobQueue( + .valkey( + valkeyClient, + configuration: .init( + queueKey: "MyJobQueue", + retentionPolicy: .init( + completedJobs: .retain, + failedJobs: .retain, + cancelledJobs: .doNotRetain + ) + ) + ), + logger: logger +) +``` + +### Job queue cleanup + +If you do opt to retain jobs after processing you will probably eventually want to clean them up. The Valkey/Redis queue provides a method `cleanup` which allows you to remove or attempt to re-run jobs based on what state they are in. You should be careful not to do anything to pending or processing jobs while the job queue is being processed as it might confuse the job processor. + +```swift +jobQueue.queue.cleanup( + pendingJobs: .doNothing, + processingJobs: .doNothing, + completedJobs: .remove(maxAge: .seconds(7*24*60*60)), + failedJobs: .rerun, + cancelledJobs: .remove, + pausedJobs: .doNothing +) +``` + +#### Scheduling cleanup + +Given this is a job you will probably want to do regularly the queue also provides a job you can use in conjunction with the `JobScheduler` that will do the cleanup for you. + +```swift +var jobSchedule = JobSchedule() +jobSchedule.addJob( + jobQueue.queue.cleanupJob, + parameters: .init(completedJobs: .remove, failedJobs: .rerun, cancelledJobs: .remove, pausedJobs: .doNothing), + schedule: .weekly(day: .sunday) +) +``` + +You can find out more about the Job scheduler in the Jobs guide + +## Topics + +### Job Queue + +- ``ValkeyJobQueue`` + +## See Also + +- ``Jobs`` +- ``JobsPostgres`` +- ``Hummingbird`` diff --git a/Package.swift b/Package.swift index 2bb8983481..4f148b283a 100644 --- a/Package.swift +++ b/Package.swift @@ -18,13 +18,13 @@ let package = Package( .package(url: "https://github.com/hummingbird-project/hummingbird-auth.git", from: "2.0.0"), .package(url: "https://github.com/hummingbird-project/hummingbird-compression.git", from: "2.0.0"), .package(url: "https://github.com/hummingbird-project/hummingbird-fluent.git", from: "2.0.0"), - .package(url: "https://github.com/hummingbird-project/swift-jobs.git", from: "1.0.0-rc"), - .package(url: "https://github.com/hummingbird-project/swift-jobs-postgres.git", from: "1.0.0-rc"), - .package(url: "https://github.com/hummingbird-project/swift-jobs-redis.git", from: "1.0.0-rc"), + .package(url: "https://github.com/hummingbird-project/swift-jobs.git", from: "1.0.0"), + .package(url: "https://github.com/hummingbird-project/swift-jobs-postgres.git", from: "1.0.0"), + .package(url: "https://github.com/hummingbird-project/swift-jobs-valkey.git", from: "1.0.0-rc.2"), .package(url: "https://github.com/hummingbird-project/hummingbird-lambda.git", from: "2.0.0-rc.5"), .package(url: "https://github.com/hummingbird-project/swift-mustache.git", from: "2.0.0"), .package(url: "https://github.com/hummingbird-project/hummingbird-postgres.git", from: "1.0.0-rc"), - .package(url: "https://github.com/hummingbird-project/postgres-migrations.git", from: "1.0.0-rc"), + .package(url: "https://github.com/hummingbird-project/postgres-migrations.git", from: "1.0.0"), .package(url: "https://github.com/hummingbird-project/hummingbird-valkey.git", from: "0.1.1"), .package(url: "https://github.com/hummingbird-project/hummingbird-websocket.git", from: "2.2.0"), .package(url: "https://github.com/hummingbird-project/swift-websocket.git", from: "1.2.0"), @@ -47,7 +47,7 @@ let package = Package( .product(name: "HummingbirdCompression", package: "hummingbird-compression"), .product(name: "HummingbirdFluent", package: "hummingbird-fluent"), .product(name: "Jobs", package: "swift-jobs"), - .product(name: "JobsRedis", package: "swift-jobs-redis"), + .product(name: "JobsValkey", package: "swift-jobs-valkey"), .product(name: "JobsPostgres", package: "swift-jobs-postgres"), .product(name: "HummingbirdLambda", package: "hummingbird-lambda"), .product(name: "HummingbirdLambdaTesting", package: "hummingbird-lambda"),