@fedify/postgres 2.0.0-dev.323 → 2.0.0-dev.335
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/dist/mq.cjs +49 -38
- package/dist/mq.js +49 -38
- package/package.json +5 -5
package/dist/mq.cjs
CHANGED
|
@@ -35,6 +35,7 @@ var PostgresMessageQueue = class {
|
|
|
35
35
|
#channelName;
|
|
36
36
|
#pollIntervalMs;
|
|
37
37
|
#initialized;
|
|
38
|
+
#initPromise;
|
|
38
39
|
#driverSerializesJson = false;
|
|
39
40
|
constructor(sql, options = {}) {
|
|
40
41
|
this.#sql = sql;
|
|
@@ -114,7 +115,7 @@ var PostgresMessageQueue = class {
|
|
|
114
115
|
const poll = async () => {
|
|
115
116
|
while (!signal?.aborted) {
|
|
116
117
|
let processed = false;
|
|
117
|
-
const
|
|
118
|
+
for (const row of await this.#sql`
|
|
118
119
|
WITH candidate AS (
|
|
119
120
|
SELECT id, ordering_key
|
|
120
121
|
FROM ${this.#sql(this.#tableName)}
|
|
@@ -127,15 +128,11 @@ var PostgresMessageQueue = class {
|
|
|
127
128
|
DELETE FROM ${this.#sql(this.#tableName)}
|
|
128
129
|
WHERE id IN (SELECT id FROM candidate)
|
|
129
130
|
RETURNING message, ordering_key;
|
|
130
|
-
|
|
131
|
-
const cancel = query.cancel.bind(query);
|
|
132
|
-
signal?.addEventListener("abort", cancel);
|
|
133
|
-
for (const row of await query) {
|
|
131
|
+
`) {
|
|
134
132
|
if (signal?.aborted) return;
|
|
135
133
|
await handler(row.message);
|
|
136
134
|
processed = true;
|
|
137
135
|
}
|
|
138
|
-
signal?.removeEventListener("abort", cancel);
|
|
139
136
|
if (processed) continue;
|
|
140
137
|
const attemptedOrderingKeys = /* @__PURE__ */ new Set();
|
|
141
138
|
while (!signal?.aborted) {
|
|
@@ -153,46 +150,57 @@ var PostgresMessageQueue = class {
|
|
|
153
150
|
const candidateId = candidate.id;
|
|
154
151
|
const orderingKey = candidate.ordering_key;
|
|
155
152
|
attemptedOrderingKeys.add(orderingKey);
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
153
|
+
const reserved = await this.#sql.reserve();
|
|
154
|
+
try {
|
|
155
|
+
const lockResult = await reserved`
|
|
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 reserved`
|
|
164
|
+
DELETE FROM ${reserved(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 reserved`
|
|
175
|
+
SELECT pg_advisory_unlock(
|
|
176
|
+
hashtext(${this.#tableName}),
|
|
177
|
+
hashtext(${orderingKey})
|
|
178
|
+
)
|
|
179
|
+
`;
|
|
173
180
|
}
|
|
174
|
-
|
|
175
|
-
await this.#sql`
|
|
176
|
-
SELECT pg_advisory_unlock(
|
|
177
|
-
hashtext(${this.#tableName}),
|
|
178
|
-
hashtext(${orderingKey})
|
|
179
|
-
)
|
|
180
|
-
`;
|
|
181
|
+
if (processed) break;
|
|
181
182
|
}
|
|
182
|
-
|
|
183
|
+
} finally {
|
|
184
|
+
reserved.release();
|
|
183
185
|
}
|
|
184
186
|
}
|
|
185
187
|
if (processed) continue;
|
|
186
188
|
break;
|
|
187
189
|
}
|
|
188
190
|
};
|
|
191
|
+
let pollLock = Promise.resolve();
|
|
192
|
+
const serializedPoll = () => {
|
|
193
|
+
const next = pollLock.then(poll);
|
|
194
|
+
pollLock = next.catch(() => {});
|
|
195
|
+
return next;
|
|
196
|
+
};
|
|
189
197
|
const timeouts = /* @__PURE__ */ new Set();
|
|
190
198
|
const listen = await this.#sql.listen(this.#channelName, async (delay) => {
|
|
191
199
|
const duration = Temporal.Duration.from(delay);
|
|
192
200
|
const durationMs = duration.total("millisecond");
|
|
193
|
-
if (durationMs < 1) await
|
|
194
|
-
else timeouts.add(setTimeout(
|
|
195
|
-
},
|
|
201
|
+
if (durationMs < 1) await serializedPoll();
|
|
202
|
+
else timeouts.add(setTimeout(serializedPoll, durationMs));
|
|
203
|
+
}, serializedPoll);
|
|
196
204
|
signal?.addEventListener("abort", () => {
|
|
197
205
|
listen.unlisten();
|
|
198
206
|
for (const timeout of timeouts) clearTimeout(timeout);
|
|
@@ -208,7 +216,7 @@ var PostgresMessageQueue = class {
|
|
|
208
216
|
timeouts.add(timeout);
|
|
209
217
|
});
|
|
210
218
|
if (timeout != null) timeouts.delete(timeout);
|
|
211
|
-
await
|
|
219
|
+
await serializedPoll();
|
|
212
220
|
}
|
|
213
221
|
await new Promise((resolve) => {
|
|
214
222
|
signal?.addEventListener("abort", () => resolve());
|
|
@@ -218,8 +226,11 @@ var PostgresMessageQueue = class {
|
|
|
218
226
|
/**
|
|
219
227
|
* Initializes the message queue table if it does not already exist.
|
|
220
228
|
*/
|
|
221
|
-
|
|
222
|
-
if (this.#initialized) return;
|
|
229
|
+
initialize() {
|
|
230
|
+
if (this.#initialized) return Promise.resolve();
|
|
231
|
+
return this.#initPromise ??= this.#doInitialize();
|
|
232
|
+
}
|
|
233
|
+
async #doInitialize() {
|
|
223
234
|
logger.debug("Initializing the message queue table {tableName}...", { tableName: this.#tableName });
|
|
224
235
|
try {
|
|
225
236
|
await this.#sql`
|
|
@@ -236,7 +247,7 @@ var PostgresMessageQueue = class {
|
|
|
236
247
|
ADD COLUMN IF NOT EXISTS ordering_key text;
|
|
237
248
|
`;
|
|
238
249
|
} catch (error) {
|
|
239
|
-
if (!(error instanceof postgres.default.PostgresError && error.constraint_name === "pg_type_typname_nsp_index")) {
|
|
250
|
+
if (!(error instanceof postgres.default.PostgresError && (error.constraint_name === "pg_type_typname_nsp_index" || error.code === "42P07"))) {
|
|
240
251
|
logger.error("Failed to initialize the message queue table: {error}", { error });
|
|
241
252
|
throw error;
|
|
242
253
|
}
|
package/dist/mq.js
CHANGED
|
@@ -34,6 +34,7 @@ var PostgresMessageQueue = class {
|
|
|
34
34
|
#channelName;
|
|
35
35
|
#pollIntervalMs;
|
|
36
36
|
#initialized;
|
|
37
|
+
#initPromise;
|
|
37
38
|
#driverSerializesJson = false;
|
|
38
39
|
constructor(sql, options = {}) {
|
|
39
40
|
this.#sql = sql;
|
|
@@ -113,7 +114,7 @@ var PostgresMessageQueue = class {
|
|
|
113
114
|
const poll = async () => {
|
|
114
115
|
while (!signal?.aborted) {
|
|
115
116
|
let processed = false;
|
|
116
|
-
const
|
|
117
|
+
for (const row of await this.#sql`
|
|
117
118
|
WITH candidate AS (
|
|
118
119
|
SELECT id, ordering_key
|
|
119
120
|
FROM ${this.#sql(this.#tableName)}
|
|
@@ -126,15 +127,11 @@ var PostgresMessageQueue = class {
|
|
|
126
127
|
DELETE FROM ${this.#sql(this.#tableName)}
|
|
127
128
|
WHERE id IN (SELECT id FROM candidate)
|
|
128
129
|
RETURNING message, ordering_key;
|
|
129
|
-
|
|
130
|
-
const cancel = query.cancel.bind(query);
|
|
131
|
-
signal?.addEventListener("abort", cancel);
|
|
132
|
-
for (const row of await query) {
|
|
130
|
+
`) {
|
|
133
131
|
if (signal?.aborted) return;
|
|
134
132
|
await handler(row.message);
|
|
135
133
|
processed = true;
|
|
136
134
|
}
|
|
137
|
-
signal?.removeEventListener("abort", cancel);
|
|
138
135
|
if (processed) continue;
|
|
139
136
|
const attemptedOrderingKeys = /* @__PURE__ */ new Set();
|
|
140
137
|
while (!signal?.aborted) {
|
|
@@ -152,46 +149,57 @@ var PostgresMessageQueue = class {
|
|
|
152
149
|
const candidateId = candidate.id;
|
|
153
150
|
const orderingKey = candidate.ordering_key;
|
|
154
151
|
attemptedOrderingKeys.add(orderingKey);
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
152
|
+
const reserved = await this.#sql.reserve();
|
|
153
|
+
try {
|
|
154
|
+
const lockResult = await reserved`
|
|
155
|
+
SELECT pg_try_advisory_lock(
|
|
156
|
+
hashtext(${this.#tableName}),
|
|
157
|
+
hashtext(${orderingKey})
|
|
158
|
+
) AS acquired
|
|
159
|
+
`;
|
|
160
|
+
if (lockResult[0].acquired) {
|
|
161
|
+
try {
|
|
162
|
+
const deleteResult = await reserved`
|
|
163
|
+
DELETE FROM ${reserved(this.#tableName)}
|
|
164
|
+
WHERE id = ${candidateId}
|
|
165
|
+
RETURNING message, ordering_key
|
|
166
|
+
`;
|
|
167
|
+
for (const row of deleteResult) {
|
|
168
|
+
if (signal?.aborted) return;
|
|
169
|
+
await handler(row.message);
|
|
170
|
+
processed = true;
|
|
171
|
+
}
|
|
172
|
+
} finally {
|
|
173
|
+
await reserved`
|
|
174
|
+
SELECT pg_advisory_unlock(
|
|
175
|
+
hashtext(${this.#tableName}),
|
|
176
|
+
hashtext(${orderingKey})
|
|
177
|
+
)
|
|
178
|
+
`;
|
|
172
179
|
}
|
|
173
|
-
|
|
174
|
-
await this.#sql`
|
|
175
|
-
SELECT pg_advisory_unlock(
|
|
176
|
-
hashtext(${this.#tableName}),
|
|
177
|
-
hashtext(${orderingKey})
|
|
178
|
-
)
|
|
179
|
-
`;
|
|
180
|
+
if (processed) break;
|
|
180
181
|
}
|
|
181
|
-
|
|
182
|
+
} finally {
|
|
183
|
+
reserved.release();
|
|
182
184
|
}
|
|
183
185
|
}
|
|
184
186
|
if (processed) continue;
|
|
185
187
|
break;
|
|
186
188
|
}
|
|
187
189
|
};
|
|
190
|
+
let pollLock = Promise.resolve();
|
|
191
|
+
const serializedPoll = () => {
|
|
192
|
+
const next = pollLock.then(poll);
|
|
193
|
+
pollLock = next.catch(() => {});
|
|
194
|
+
return next;
|
|
195
|
+
};
|
|
188
196
|
const timeouts = /* @__PURE__ */ new Set();
|
|
189
197
|
const listen = await this.#sql.listen(this.#channelName, async (delay) => {
|
|
190
198
|
const duration = Temporal.Duration.from(delay);
|
|
191
199
|
const durationMs = duration.total("millisecond");
|
|
192
|
-
if (durationMs < 1) await
|
|
193
|
-
else timeouts.add(setTimeout(
|
|
194
|
-
},
|
|
200
|
+
if (durationMs < 1) await serializedPoll();
|
|
201
|
+
else timeouts.add(setTimeout(serializedPoll, durationMs));
|
|
202
|
+
}, serializedPoll);
|
|
195
203
|
signal?.addEventListener("abort", () => {
|
|
196
204
|
listen.unlisten();
|
|
197
205
|
for (const timeout of timeouts) clearTimeout(timeout);
|
|
@@ -207,7 +215,7 @@ var PostgresMessageQueue = class {
|
|
|
207
215
|
timeouts.add(timeout);
|
|
208
216
|
});
|
|
209
217
|
if (timeout != null) timeouts.delete(timeout);
|
|
210
|
-
await
|
|
218
|
+
await serializedPoll();
|
|
211
219
|
}
|
|
212
220
|
await new Promise((resolve) => {
|
|
213
221
|
signal?.addEventListener("abort", () => resolve());
|
|
@@ -217,8 +225,11 @@ var PostgresMessageQueue = class {
|
|
|
217
225
|
/**
|
|
218
226
|
* Initializes the message queue table if it does not already exist.
|
|
219
227
|
*/
|
|
220
|
-
|
|
221
|
-
if (this.#initialized) return;
|
|
228
|
+
initialize() {
|
|
229
|
+
if (this.#initialized) return Promise.resolve();
|
|
230
|
+
return this.#initPromise ??= this.#doInitialize();
|
|
231
|
+
}
|
|
232
|
+
async #doInitialize() {
|
|
222
233
|
logger.debug("Initializing the message queue table {tableName}...", { tableName: this.#tableName });
|
|
223
234
|
try {
|
|
224
235
|
await this.#sql`
|
|
@@ -235,7 +246,7 @@ var PostgresMessageQueue = class {
|
|
|
235
246
|
ADD COLUMN IF NOT EXISTS ordering_key text;
|
|
236
247
|
`;
|
|
237
248
|
} catch (error) {
|
|
238
|
-
if (!(error instanceof postgres.PostgresError && error.constraint_name === "pg_type_typname_nsp_index")) {
|
|
249
|
+
if (!(error instanceof postgres.PostgresError && (error.constraint_name === "pg_type_typname_nsp_index" || error.code === "42P07"))) {
|
|
239
250
|
logger.error("Failed to initialize the message queue table: {error}", { error });
|
|
240
251
|
throw error;
|
|
241
252
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/postgres",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.335+6fe86704",
|
|
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.
|
|
77
|
+
"@fedify/fedify": "^2.0.0-dev.335+6fe86704"
|
|
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
|
-
"@fedify/
|
|
84
|
-
"@fedify/
|
|
83
|
+
"@fedify/testing": "^2.0.0-dev.335+6fe86704",
|
|
84
|
+
"@fedify/fixture": "^2.0.0"
|
|
85
85
|
},
|
|
86
86
|
"scripts": {
|
|
87
87
|
"build:self": "tsdown",
|
|
@@ -90,6 +90,6 @@
|
|
|
90
90
|
"pretest": "pnpm build",
|
|
91
91
|
"test": "node --experimental-transform-types --test",
|
|
92
92
|
"pretest:bun": "pnpm build",
|
|
93
|
-
"test:bun": "bun test --timeout=
|
|
93
|
+
"test:bun": "bun test --timeout=60000"
|
|
94
94
|
}
|
|
95
95
|
}
|