@fedify/postgres 2.1.0-dev.418 → 2.1.0-dev.513
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 +30 -11
- package/dist/mq.d.cts +13 -0
- package/dist/mq.d.ts +13 -0
- package/dist/mq.js +30 -11
- package/package.json +3 -3
package/dist/mq.cjs
CHANGED
|
@@ -17,6 +17,15 @@ const INITIALIZE_BACKOFF_MS = 10;
|
|
|
17
17
|
function sleep(milliseconds) {
|
|
18
18
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
19
19
|
}
|
|
20
|
+
function withTimeout(result, timeoutMs) {
|
|
21
|
+
const resolved = Promise.resolve(result);
|
|
22
|
+
if (timeoutMs <= 0) return resolved;
|
|
23
|
+
let timer;
|
|
24
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
25
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Message handler timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
26
|
+
});
|
|
27
|
+
return Promise.race([resolved, timeoutPromise]).finally(() => clearTimeout(timer));
|
|
28
|
+
}
|
|
20
29
|
function isInitializationRaceError(error) {
|
|
21
30
|
return error instanceof postgres.default.PostgresError && (error.constraint_name === "pg_type_typname_nsp_index" || error.constraint_name === "pg_class_relname_nsp_index" || error.code === "42P07" || error.code === "42710");
|
|
22
31
|
}
|
|
@@ -47,6 +56,7 @@ var PostgresMessageQueue = class {
|
|
|
47
56
|
#tableName;
|
|
48
57
|
#channelName;
|
|
49
58
|
#pollIntervalMs;
|
|
59
|
+
#handlerTimeoutMs;
|
|
50
60
|
#initialized;
|
|
51
61
|
#initPromise;
|
|
52
62
|
#driverSerializesJson = false;
|
|
@@ -55,6 +65,7 @@ var PostgresMessageQueue = class {
|
|
|
55
65
|
this.#tableName = options?.tableName ?? "fedify_message_v2";
|
|
56
66
|
this.#channelName = options?.channelName ?? "fedify_channel";
|
|
57
67
|
this.#pollIntervalMs = Temporal.Duration.from(options?.pollInterval ?? { seconds: 5 }).total("millisecond");
|
|
68
|
+
this.#handlerTimeoutMs = Temporal.Duration.from(options?.handlerTimeout ?? { seconds: 60 }).total("millisecond");
|
|
58
69
|
this.#initialized = options?.initialized ?? false;
|
|
59
70
|
}
|
|
60
71
|
async enqueue(message, options) {
|
|
@@ -143,7 +154,7 @@ var PostgresMessageQueue = class {
|
|
|
143
154
|
RETURNING message, ordering_key;
|
|
144
155
|
`) {
|
|
145
156
|
if (signal?.aborted) return;
|
|
146
|
-
await handler(row.message);
|
|
157
|
+
await withTimeout(handler(row.message), this.#handlerTimeoutMs);
|
|
147
158
|
processed = true;
|
|
148
159
|
}
|
|
149
160
|
if (processed) continue;
|
|
@@ -180,7 +191,7 @@ var PostgresMessageQueue = class {
|
|
|
180
191
|
`;
|
|
181
192
|
for (const row of deleteResult) {
|
|
182
193
|
if (signal?.aborted) return;
|
|
183
|
-
await handler(row.message);
|
|
194
|
+
await withTimeout(handler(row.message), this.#handlerTimeoutMs);
|
|
184
195
|
processed = true;
|
|
185
196
|
}
|
|
186
197
|
} finally {
|
|
@@ -219,15 +230,23 @@ var PostgresMessageQueue = class {
|
|
|
219
230
|
};
|
|
220
231
|
const timeouts = /* @__PURE__ */ new Set();
|
|
221
232
|
const listen = await this.#sql.listen(this.#channelName, async (delay) => {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
233
|
+
try {
|
|
234
|
+
const duration = Temporal.Duration.from(delay);
|
|
235
|
+
const durationMs = duration.total("millisecond");
|
|
236
|
+
if (durationMs < 1) await safeSerializedPoll("notify-immediate");
|
|
237
|
+
else {
|
|
238
|
+
const timeout = setTimeout(() => {
|
|
239
|
+
timeouts.delete(timeout);
|
|
240
|
+
safeSerializedPoll("notify-delayed");
|
|
241
|
+
}, durationMs);
|
|
242
|
+
timeouts.add(timeout);
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
logger.error("Error parsing NOTIFY payload {delay}: {error}", {
|
|
246
|
+
delay,
|
|
247
|
+
error
|
|
248
|
+
});
|
|
249
|
+
await safeSerializedPoll("notify-fallback");
|
|
231
250
|
}
|
|
232
251
|
}, () => safeSerializedPoll("subscribe"));
|
|
233
252
|
signal?.addEventListener("abort", () => {
|
package/dist/mq.d.cts
CHANGED
|
@@ -28,6 +28,19 @@ interface PostgresMessageQueueOptions {
|
|
|
28
28
|
* @default `{ seconds: 5 }`
|
|
29
29
|
*/
|
|
30
30
|
readonly pollInterval?: Temporal.Duration | Temporal.DurationLike;
|
|
31
|
+
/**
|
|
32
|
+
* The maximum time to wait for a message handler to complete before
|
|
33
|
+
* considering it hung. When a handler exceeds this timeout, it is
|
|
34
|
+
* treated as an error and the poll loop moves on to the next message,
|
|
35
|
+
* preventing a single hung handler from permanently blocking the queue.
|
|
36
|
+
*
|
|
37
|
+
* Set to zero to disable the timeout (not recommended in production).
|
|
38
|
+
*
|
|
39
|
+
* 60 seconds by default.
|
|
40
|
+
* @default `{ seconds: 60 }`
|
|
41
|
+
* @since 2.0.3
|
|
42
|
+
*/
|
|
43
|
+
readonly handlerTimeout?: Temporal.Duration | Temporal.DurationLike;
|
|
31
44
|
}
|
|
32
45
|
/**
|
|
33
46
|
* A message queue that uses PostgreSQL as the underlying storage.
|
package/dist/mq.d.ts
CHANGED
|
@@ -29,6 +29,19 @@ interface PostgresMessageQueueOptions {
|
|
|
29
29
|
* @default `{ seconds: 5 }`
|
|
30
30
|
*/
|
|
31
31
|
readonly pollInterval?: Temporal.Duration | Temporal.DurationLike;
|
|
32
|
+
/**
|
|
33
|
+
* The maximum time to wait for a message handler to complete before
|
|
34
|
+
* considering it hung. When a handler exceeds this timeout, it is
|
|
35
|
+
* treated as an error and the poll loop moves on to the next message,
|
|
36
|
+
* preventing a single hung handler from permanently blocking the queue.
|
|
37
|
+
*
|
|
38
|
+
* Set to zero to disable the timeout (not recommended in production).
|
|
39
|
+
*
|
|
40
|
+
* 60 seconds by default.
|
|
41
|
+
* @default `{ seconds: 60 }`
|
|
42
|
+
* @since 2.0.3
|
|
43
|
+
*/
|
|
44
|
+
readonly handlerTimeout?: Temporal.Duration | Temporal.DurationLike;
|
|
32
45
|
}
|
|
33
46
|
/**
|
|
34
47
|
* A message queue that uses PostgreSQL as the underlying storage.
|
package/dist/mq.js
CHANGED
|
@@ -16,6 +16,15 @@ const INITIALIZE_BACKOFF_MS = 10;
|
|
|
16
16
|
function sleep(milliseconds) {
|
|
17
17
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
18
18
|
}
|
|
19
|
+
function withTimeout(result, timeoutMs) {
|
|
20
|
+
const resolved = Promise.resolve(result);
|
|
21
|
+
if (timeoutMs <= 0) return resolved;
|
|
22
|
+
let timer;
|
|
23
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
24
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Message handler timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
25
|
+
});
|
|
26
|
+
return Promise.race([resolved, timeoutPromise]).finally(() => clearTimeout(timer));
|
|
27
|
+
}
|
|
19
28
|
function isInitializationRaceError(error) {
|
|
20
29
|
return error instanceof postgres.PostgresError && (error.constraint_name === "pg_type_typname_nsp_index" || error.constraint_name === "pg_class_relname_nsp_index" || error.code === "42P07" || error.code === "42710");
|
|
21
30
|
}
|
|
@@ -46,6 +55,7 @@ var PostgresMessageQueue = class {
|
|
|
46
55
|
#tableName;
|
|
47
56
|
#channelName;
|
|
48
57
|
#pollIntervalMs;
|
|
58
|
+
#handlerTimeoutMs;
|
|
49
59
|
#initialized;
|
|
50
60
|
#initPromise;
|
|
51
61
|
#driverSerializesJson = false;
|
|
@@ -54,6 +64,7 @@ var PostgresMessageQueue = class {
|
|
|
54
64
|
this.#tableName = options?.tableName ?? "fedify_message_v2";
|
|
55
65
|
this.#channelName = options?.channelName ?? "fedify_channel";
|
|
56
66
|
this.#pollIntervalMs = Temporal.Duration.from(options?.pollInterval ?? { seconds: 5 }).total("millisecond");
|
|
67
|
+
this.#handlerTimeoutMs = Temporal.Duration.from(options?.handlerTimeout ?? { seconds: 60 }).total("millisecond");
|
|
57
68
|
this.#initialized = options?.initialized ?? false;
|
|
58
69
|
}
|
|
59
70
|
async enqueue(message, options) {
|
|
@@ -142,7 +153,7 @@ var PostgresMessageQueue = class {
|
|
|
142
153
|
RETURNING message, ordering_key;
|
|
143
154
|
`) {
|
|
144
155
|
if (signal?.aborted) return;
|
|
145
|
-
await handler(row.message);
|
|
156
|
+
await withTimeout(handler(row.message), this.#handlerTimeoutMs);
|
|
146
157
|
processed = true;
|
|
147
158
|
}
|
|
148
159
|
if (processed) continue;
|
|
@@ -179,7 +190,7 @@ var PostgresMessageQueue = class {
|
|
|
179
190
|
`;
|
|
180
191
|
for (const row of deleteResult) {
|
|
181
192
|
if (signal?.aborted) return;
|
|
182
|
-
await handler(row.message);
|
|
193
|
+
await withTimeout(handler(row.message), this.#handlerTimeoutMs);
|
|
183
194
|
processed = true;
|
|
184
195
|
}
|
|
185
196
|
} finally {
|
|
@@ -218,15 +229,23 @@ var PostgresMessageQueue = class {
|
|
|
218
229
|
};
|
|
219
230
|
const timeouts = /* @__PURE__ */ new Set();
|
|
220
231
|
const listen = await this.#sql.listen(this.#channelName, async (delay) => {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
232
|
+
try {
|
|
233
|
+
const duration = Temporal.Duration.from(delay);
|
|
234
|
+
const durationMs = duration.total("millisecond");
|
|
235
|
+
if (durationMs < 1) await safeSerializedPoll("notify-immediate");
|
|
236
|
+
else {
|
|
237
|
+
const timeout = setTimeout(() => {
|
|
238
|
+
timeouts.delete(timeout);
|
|
239
|
+
safeSerializedPoll("notify-delayed");
|
|
240
|
+
}, durationMs);
|
|
241
|
+
timeouts.add(timeout);
|
|
242
|
+
}
|
|
243
|
+
} catch (error) {
|
|
244
|
+
logger.error("Error parsing NOTIFY payload {delay}: {error}", {
|
|
245
|
+
delay,
|
|
246
|
+
error
|
|
247
|
+
});
|
|
248
|
+
await safeSerializedPoll("notify-fallback");
|
|
230
249
|
}
|
|
231
250
|
}, () => safeSerializedPoll("subscribe"));
|
|
232
251
|
signal?.addEventListener("abort", () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/postgres",
|
|
3
|
-
"version": "2.1.0-dev.
|
|
3
|
+
"version": "2.1.0-dev.513+f5543fca",
|
|
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.1.0-dev.
|
|
77
|
+
"@fedify/fedify": "^2.1.0-dev.513+f5543fca"
|
|
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.1.0-dev.
|
|
84
|
+
"@fedify/testing": "^2.1.0-dev.513+f5543fca"
|
|
85
85
|
},
|
|
86
86
|
"scripts": {
|
|
87
87
|
"build:self": "tsdown",
|