velocious 1.0.540 → 1.0.542

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "velocious": "build/bin/velocious.js"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.540",
6
+ "version": "1.0.542",
7
7
  "main": "build/index.js",
8
8
  "types": "build/index.d.ts",
9
9
  "files": [
@@ -146,14 +146,16 @@ export default class BackgroundJobsStore {
146
146
  await this._withDb(async (db) => {
147
147
  if (options?.deduplicateWhileQueued) {
148
148
  // Dedupe on the job's identity (name + args + queue), NOT its concurrency key, so a job
149
- // keeps whatever concurrency it resolves to in particular a scheduled job that relies on
150
- // its queue-derived `queue:<name>` cap still participates in that cluster-wide cap instead
151
- // of being pulled onto a private per-job key.
149
+ // keeps whatever concurrency it resolves to. Only an existing job scheduled no later than
150
+ // this enqueue can cover it; a retry backed off into the future must not suppress earlier
151
+ // work. Ordering returns the earliest covering job when several queued rows already exist.
152
152
  const existing = await db
153
153
  .newQuery()
154
154
  .from(JOBS_TABLE)
155
155
  .select("id")
156
156
  .where({status: "queued", job_name: jobName, args_json: argsJson, queue})
157
+ .where(`scheduled_at_ms <= ${db.quote(scheduledAtMs)}`)
158
+ .order("scheduled_at_ms ASC")
157
159
  .limit(1)
158
160
  .results()
159
161
 
@@ -15,7 +15,7 @@
15
15
  * @property {string} [queue] - Queue name. Defaults to `"default"`. When the queue has a configured cap in `backgroundJobs.queues`, that cap is enforced cluster-wide.
16
16
  * @property {string} [concurrencyKey] - Opaque non-empty key used to share a concurrency cap. Overrides any queue-derived cap.
17
17
  * @property {number} [maxConcurrency] - Positive integer cap; must be paired with `concurrencyKey`.
18
- * @property {boolean} [deduplicateWhileQueued] - When true, skip the enqueue if an identical still-queued job (same job name, args and queue) already exists, returning that job's id. Deduplication is by job identity and independent of `concurrencyKey`, so the job keeps its normal (e.g. queue-derived) concurrency cap. Keeps an interval-scheduled recurring job (e.g. retention pruning) from piling up redundant queued rows when it runs slower than its interval or no worker is free.
18
+ * @property {boolean} [deduplicateWhileQueued] - When true, skip the enqueue if an identical still-queued job (same job name, args and queue) is scheduled no later than this enqueue, returning the earliest matching job's id. A future retry does not suppress earlier work. Deduplication is independent of `concurrencyKey`, so the job keeps its normal (e.g. queue-derived) concurrency cap. Keeps an interval-scheduled recurring job (e.g. retention pruning) from piling up redundant queued rows when it runs slower than its interval or no worker is free.
19
19
  * @property {number} [scheduledAtMs] - Epoch timestamp in milliseconds when the job becomes eligible for dispatch. Defaults to enqueue time.
20
20
  */
21
21
  /**
@@ -3167,6 +3167,47 @@ class VelociousDatabaseRecord {
3167
3167
  return await this._newQuery().findByOrFail(conditions)
3168
3168
  }
3169
3169
 
3170
+ /**
3171
+ * Returns a scope whose eager finders run against an explicit `tenant` (and
3172
+ * therefore its database) instead of whatever tenant is ambient in
3173
+ * `Current.tenant()`. Use it to read a model that may live in a specific
3174
+ * tenant or in the default database from another tenant context — for example
3175
+ * `GithubWebhook.usingTenant(tenant).findBy({id})` — without depending on
3176
+ * which tenant happens to be active. The target tenant's connections are
3177
+ * ensured for the duration of each query.
3178
+ * @template {typeof VelociousDatabaseRecord} MC
3179
+ * @this {MC}
3180
+ * @param {?} tenant - Tenant descriptor to scope the queries to (as accepted by `configuration.runWithTenant`).
3181
+ * @returns {{find: (recordId: ?) => Promise<InstanceType<MC> | null>, findBy: (conditions: {[key: string]: string | number}) => Promise<InstanceType<MC> | null>, findByOrFail: (conditions: {[key: string]: string | number}) => Promise<InstanceType<MC>>}} - Eager finders scoped to the given tenant.
3182
+ */
3183
+ static usingTenant(tenant) {
3184
+ const ModelClass = this
3185
+
3186
+ return {
3187
+ find: (recordId) => ModelClass._runUsingTenant(tenant, () => ModelClass.find(recordId)),
3188
+ findBy: (conditions) => ModelClass._runUsingTenant(tenant, () => ModelClass.findBy(conditions)),
3189
+ findByOrFail: (conditions) => ModelClass._runUsingTenant(tenant, () => ModelClass.findByOrFail(conditions))
3190
+ }
3191
+ }
3192
+
3193
+ /**
3194
+ * Runs `callback` with the tenant switched to `tenant` and that tenant's
3195
+ * connections ensured. Backs `usingTenant`.
3196
+ * @template T
3197
+ * @param {?} tenant - Tenant descriptor.
3198
+ * @param {() => Promise<T>} callback - Query to run under the tenant.
3199
+ * @returns {Promise<T>} - Resolves with the callback's result.
3200
+ */
3201
+ static async _runUsingTenant(tenant, callback) {
3202
+ const configuration = this._getConfiguration()
3203
+
3204
+ // Do NOT ensureInitialized() out here: for a tenant-switched model whose
3205
+ // first initialization would resolve metadata from the ambient tenant's
3206
+ // database, that must happen under the requested tenant. The finders inside
3207
+ // `callback` call ensureInitialized() themselves, now within this scope.
3208
+ return await configuration.runWithTenant(tenant, () => configuration.ensureConnections({name: `usingTenant: ${this.getModelName()}`}, callback))
3209
+ }
3210
+
3170
3211
  /**
3171
3212
  * Runs find or create by.
3172
3213
  * @template {typeof VelociousDatabaseRecord} MC