@fedify/postgres 2.0.0-dev.279 → 2.0.0-dev.323

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.
Files changed (3) hide show
  1. package/dist/mq.cjs +53 -18
  2. package/dist/mq.js +53 -18
  3. package/package.json +3 -3
package/dist/mq.cjs CHANGED
@@ -113,42 +113,77 @@ var PostgresMessageQueue = class {
113
113
  const { signal } = options;
114
114
  const poll = async () => {
115
115
  while (!signal?.aborted) {
116
+ let processed = false;
116
117
  const query = this.#sql`
117
- DELETE FROM ${this.#sql(this.#tableName)}
118
- WHERE id = (
119
- SELECT id
118
+ WITH candidate AS (
119
+ SELECT id, ordering_key
120
120
  FROM ${this.#sql(this.#tableName)}
121
121
  WHERE created + delay < CURRENT_TIMESTAMP
122
- AND (ordering_key IS NULL
123
- OR pg_try_advisory_lock(
124
- hashtext(${this.#tableName}),
125
- hashtext(ordering_key)
126
- ))
122
+ AND ordering_key IS NULL
127
123
  ORDER BY created
128
124
  LIMIT 1
125
+ FOR UPDATE SKIP LOCKED
129
126
  )
127
+ DELETE FROM ${this.#sql(this.#tableName)}
128
+ WHERE id IN (SELECT id FROM candidate)
130
129
  RETURNING message, ordering_key;
131
130
  `.execute();
132
131
  const cancel = query.cancel.bind(query);
133
132
  signal?.addEventListener("abort", cancel);
134
- let i = 0;
135
133
  for (const row of await query) {
136
134
  if (signal?.aborted) return;
137
- const orderingKey = row.ordering_key;
138
- try {
139
- await handler(row.message);
140
- } finally {
141
- if (orderingKey != null) await this.#sql`
135
+ await handler(row.message);
136
+ processed = true;
137
+ }
138
+ signal?.removeEventListener("abort", cancel);
139
+ if (processed) continue;
140
+ const attemptedOrderingKeys = /* @__PURE__ */ new Set();
141
+ while (!signal?.aborted) {
142
+ const candidateResult = await this.#sql`
143
+ SELECT id, ordering_key
144
+ FROM ${this.#sql(this.#tableName)}
145
+ WHERE created + delay < CURRENT_TIMESTAMP
146
+ AND ordering_key IS NOT NULL
147
+ ${attemptedOrderingKeys.size > 0 ? this.#sql`AND ordering_key NOT IN ${this.#sql([...attemptedOrderingKeys])}` : this.#sql``}
148
+ ORDER BY created
149
+ LIMIT 1
150
+ `;
151
+ if (candidateResult.length === 0) break;
152
+ const candidate = candidateResult[0];
153
+ const candidateId = candidate.id;
154
+ const orderingKey = candidate.ordering_key;
155
+ attemptedOrderingKeys.add(orderingKey);
156
+ const lockResult = await this.#sql`
157
+ SELECT pg_try_advisory_lock(
158
+ hashtext(${this.#tableName}),
159
+ hashtext(${orderingKey})
160
+ ) AS acquired
161
+ `;
162
+ if (lockResult[0].acquired) {
163
+ try {
164
+ const deleteResult = await this.#sql`
165
+ DELETE FROM ${this.#sql(this.#tableName)}
166
+ WHERE id = ${candidateId}
167
+ RETURNING message, ordering_key
168
+ `;
169
+ for (const row of deleteResult) {
170
+ if (signal?.aborted) return;
171
+ await handler(row.message);
172
+ processed = true;
173
+ }
174
+ } finally {
175
+ await this.#sql`
142
176
  SELECT pg_advisory_unlock(
143
177
  hashtext(${this.#tableName}),
144
178
  hashtext(${orderingKey})
145
- );
179
+ )
146
180
  `;
181
+ }
182
+ if (processed) break;
147
183
  }
148
- i++;
149
184
  }
150
- signal?.removeEventListener("abort", cancel);
151
- if (i < 1) break;
185
+ if (processed) continue;
186
+ break;
152
187
  }
153
188
  };
154
189
  const timeouts = /* @__PURE__ */ new Set();
package/dist/mq.js CHANGED
@@ -112,42 +112,77 @@ var PostgresMessageQueue = class {
112
112
  const { signal } = options;
113
113
  const poll = async () => {
114
114
  while (!signal?.aborted) {
115
+ let processed = false;
115
116
  const query = this.#sql`
116
- DELETE FROM ${this.#sql(this.#tableName)}
117
- WHERE id = (
118
- SELECT id
117
+ WITH candidate AS (
118
+ SELECT id, ordering_key
119
119
  FROM ${this.#sql(this.#tableName)}
120
120
  WHERE created + delay < CURRENT_TIMESTAMP
121
- AND (ordering_key IS NULL
122
- OR pg_try_advisory_lock(
123
- hashtext(${this.#tableName}),
124
- hashtext(ordering_key)
125
- ))
121
+ AND ordering_key IS NULL
126
122
  ORDER BY created
127
123
  LIMIT 1
124
+ FOR UPDATE SKIP LOCKED
128
125
  )
126
+ DELETE FROM ${this.#sql(this.#tableName)}
127
+ WHERE id IN (SELECT id FROM candidate)
129
128
  RETURNING message, ordering_key;
130
129
  `.execute();
131
130
  const cancel = query.cancel.bind(query);
132
131
  signal?.addEventListener("abort", cancel);
133
- let i = 0;
134
132
  for (const row of await query) {
135
133
  if (signal?.aborted) return;
136
- const orderingKey = row.ordering_key;
137
- try {
138
- await handler(row.message);
139
- } finally {
140
- if (orderingKey != null) await this.#sql`
134
+ await handler(row.message);
135
+ processed = true;
136
+ }
137
+ signal?.removeEventListener("abort", cancel);
138
+ if (processed) continue;
139
+ const attemptedOrderingKeys = /* @__PURE__ */ new Set();
140
+ while (!signal?.aborted) {
141
+ const candidateResult = await this.#sql`
142
+ SELECT id, ordering_key
143
+ FROM ${this.#sql(this.#tableName)}
144
+ WHERE created + delay < CURRENT_TIMESTAMP
145
+ AND ordering_key IS NOT NULL
146
+ ${attemptedOrderingKeys.size > 0 ? this.#sql`AND ordering_key NOT IN ${this.#sql([...attemptedOrderingKeys])}` : this.#sql``}
147
+ ORDER BY created
148
+ LIMIT 1
149
+ `;
150
+ if (candidateResult.length === 0) break;
151
+ const candidate = candidateResult[0];
152
+ const candidateId = candidate.id;
153
+ const orderingKey = candidate.ordering_key;
154
+ attemptedOrderingKeys.add(orderingKey);
155
+ const lockResult = await this.#sql`
156
+ SELECT pg_try_advisory_lock(
157
+ hashtext(${this.#tableName}),
158
+ hashtext(${orderingKey})
159
+ ) AS acquired
160
+ `;
161
+ if (lockResult[0].acquired) {
162
+ try {
163
+ const deleteResult = await this.#sql`
164
+ DELETE FROM ${this.#sql(this.#tableName)}
165
+ WHERE id = ${candidateId}
166
+ RETURNING message, ordering_key
167
+ `;
168
+ for (const row of deleteResult) {
169
+ if (signal?.aborted) return;
170
+ await handler(row.message);
171
+ processed = true;
172
+ }
173
+ } finally {
174
+ await this.#sql`
141
175
  SELECT pg_advisory_unlock(
142
176
  hashtext(${this.#tableName}),
143
177
  hashtext(${orderingKey})
144
- );
178
+ )
145
179
  `;
180
+ }
181
+ if (processed) break;
146
182
  }
147
- i++;
148
183
  }
149
- signal?.removeEventListener("abort", cancel);
150
- if (i < 1) break;
184
+ if (processed) continue;
185
+ break;
151
186
  }
152
187
  };
153
188
  const timeouts = /* @__PURE__ */ new Set();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/postgres",
3
- "version": "2.0.0-dev.279+ce1bdc22",
3
+ "version": "2.0.0-dev.323+1d796545",
4
4
  "description": "PostgreSQL drivers for Fedify",
5
5
  "keywords": [
6
6
  "fedify",
@@ -74,14 +74,14 @@
74
74
  },
75
75
  "peerDependencies": {
76
76
  "postgres": "^3.4.7",
77
- "@fedify/fedify": "^2.0.0-dev.279+ce1bdc22"
77
+ "@fedify/fedify": "^2.0.0-dev.323+1d796545"
78
78
  },
79
79
  "devDependencies": {
80
80
  "@std/async": "npm:@jsr/std__async@^1.0.13",
81
81
  "tsdown": "^0.12.9",
82
82
  "typescript": "^5.9.3",
83
83
  "@fedify/fixture": "^2.0.0",
84
- "@fedify/testing": "^2.0.0-dev.279+ce1bdc22"
84
+ "@fedify/testing": "^2.0.0-dev.323+1d796545"
85
85
  },
86
86
  "scripts": {
87
87
  "build:self": "tsdown",