You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat(backend): add BullMQ JobManager framework
Introduces a JobManager over BullMQ: work-queue Workloads with a ProcessContext, CronWorkloads multiplexed onto a shared cron worker with a reconcile() sweep helper, a JobProducer that owns the queues and the deduplicated enqueue path, and a Redis-backed read model (status/jobDetail). Wired into the backend entrypoint with a demo workload.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* wip
* further wip
* further wip
* migrated repo indexing to workload
* further wip
* simplicity: remove much of the web changes
* wip
* add necessary lifecyle hooks
* further wip
* further wip
* migrate attachment & audit log pruning to workload system
* prioritize initial repository indexing
* update retry behaviour
* add locking
* improve logging s.t., we use a log context
* fix: make job scheduler upserts idempotent
* fix: clean up repos from deleted connections
* refactor: rename job scheduler reconciliation
* fix: tolerate missing account permission sync jobs
* fix: address background sync edge cases
* docs: add job manager changelog entry
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+3Lines changed: 3 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
### Changed
11
+
- Migrated connection syncing, repository indexing, permission syncing, and background pruning from in-process managers and pollers to BullMQ workloads with retries and per-resource execution locking. [#1427](https://github.com/sourcebot-dev/sourcebot/pull/1427)
12
+
10
13
### Fixed
11
14
- Upgraded Next.js to 16.3.1 to bound memory retained by high-cardinality dynamic route cache entries. [#1594](https://github.com/sourcebot-dev/sourcebot/pull/1594)
12
15
- Fixed memory leak attributed to CodeMirror allocating objects on heap that were never freed. [#1580](https://github.com/sourcebot-dev/sourcebot/pull/1580)
Copy file name to clipboardExpand all lines: CLAUDE.md
+20Lines changed: 20 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,6 +18,26 @@ To build a specific package:
18
18
yarn workspace @sourcebot/<package-name> build
19
19
```
20
20
21
+
## Backend Workloads
22
+
23
+
Use the workload system in `packages/backend` for background work. Define the queue payload and default job behavior in the shared queue registry, implement a `Workload`, and register it with the `JobManager`.
24
+
25
+
### Execution locks
26
+
27
+
- Key an execution lock by the logical resource being mutated, not by the job ID. Workloads that mutate the same resource must use the exact same lock key. For example, repo indexing and repo cleanup share the per-repo filesystem and search-index lock, while repo permission syncing uses a separate per-repo permission lock.
28
+
- An execution lock serializes work but does not deduplicate it. Multiple jobs for one resource may still be queued and will execute one at a time.
29
+
- The lock lease is extended automatically while work is running. The workload's `AbortSignal` is aborted if extension fails or the worker shuts down.
30
+
- Abortion is cooperative. Call `signal.throwIfAborted()` before side effects and after long-running or external operations so work stops promptly after losing the lock. The signal cannot cancel an operation that has already been submitted.
31
+
-`onStarted` runs after the execution lock is acquired and immediately before `process`. `onCompleted` and `onTerminalFailure` are BullMQ event hooks and run after the processor has returned and released the lock.
32
+
33
+
### Lifecycle state
34
+
35
+
- In `onStarted`, upsert the workload-specific job row as `IN_PROGRESS`. If the parent resource tracks a `latest...JobId`, update that pointer in the same database transaction.
36
+
- Completion and terminal-failure hooks must always update their own historical job row by job ID. Do not condition that update on the job still being latest. Every job row should record its actual outcome.
37
+
-`onTerminalFailure` only runs after the job exhausts all retry attempts. Intermediate failures are retried without marking the lifecycle row as terminally failed.
38
+
- If a completion or failure hook publishes state onto the parent resource, use a conditional `updateMany` keyed by both the resource ID and its `latest...JobId`. This prevents an older hook from overwriting state belonging to a newer job after the execution lock has been released.
39
+
- Parent-resource state written inside `process` is already serialized by the execution lock. It does not need a latest-job conditional merely because the resource tracks the latest job ID.
40
+
21
41
## File Naming
22
42
23
43
Files should use camelCase starting with a lowercase letter:
Copy file name to clipboardExpand all lines: docs/snippets/schemas/v3/index.schema.mdx
+12-6Lines changed: 12 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -32,12 +32,14 @@
32
32
"resyncConnectionPollingIntervalMs": {
33
33
"type": "number",
34
34
"description": "The polling rate (in milliseconds) at which the db should be checked for connections that need to be re-synced. Defaults to 1 second.",
35
-
"minimum": 1
35
+
"minimum": 1,
36
+
"deprecated": true
36
37
},
37
38
"reindexRepoPollingIntervalMs": {
38
39
"type": "number",
39
40
"description": "The polling rate (in milliseconds) at which the db should be checked for repos that should be re-indexed. Defaults to 1 second.",
40
-
"minimum": 1
41
+
"minimum": 1,
42
+
"deprecated": true
41
43
},
42
44
"maxConnectionSyncJobConcurrency": {
43
45
"type": "number",
@@ -52,7 +54,8 @@
52
54
"maxRepoGarbageCollectionJobConcurrency": {
53
55
"type": "number",
54
56
"description": "The number of repo GC jobs to run concurrently. Defaults to 8.",
55
-
"minimum": 1
57
+
"minimum": 1,
58
+
"deprecated": true
56
59
},
57
60
"repoGarbageCollectionGracePeriodMs": {
58
61
"type": "number",
@@ -216,12 +219,14 @@
216
219
"resyncConnectionPollingIntervalMs": {
217
220
"type": "number",
218
221
"description": "The polling rate (in milliseconds) at which the db should be checked for connections that need to be re-synced. Defaults to 1 second.",
219
-
"minimum": 1
222
+
"minimum": 1,
223
+
"deprecated": true
220
224
},
221
225
"reindexRepoPollingIntervalMs": {
222
226
"type": "number",
223
227
"description": "The polling rate (in milliseconds) at which the db should be checked for repos that should be re-indexed. Defaults to 1 second.",
224
-
"minimum": 1
228
+
"minimum": 1,
229
+
"deprecated": true
225
230
},
226
231
"maxConnectionSyncJobConcurrency": {
227
232
"type": "number",
@@ -236,7 +241,8 @@
236
241
"maxRepoGarbageCollectionJobConcurrency": {
237
242
"type": "number",
238
243
"description": "The number of repo GC jobs to run concurrently. Defaults to 8.",
0 commit comments