@ahrowe/mongo 0.1.3 → 0.2.1
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/CHANGELOG.md +76 -0
- package/README.md +50 -23
- package/dist/db.d.ts +31 -13
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +163 -130
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/outboxRelay.d.ts +18 -26
- package/dist/outboxRelay.d.ts.map +1 -1
- package/dist/outboxRelay.js +78 -56
- package/dist/outboxRelay.js.map +1 -1
- package/dist/types/outbox.d.ts +13 -2
- package/dist/types/outbox.d.ts.map +1 -1
- package/dist/types/outbox.js +4 -1
- package/dist/types/outbox.js.map +1 -1
- package/docs/CLAUDE.md +112 -86
- package/package.json +5 -2
- package/dist/batchify.d.ts +0 -36
- package/dist/batchify.d.ts.map +0 -1
- package/dist/batchify.js +0 -57
- package/dist/batchify.js.map +0 -1
package/dist/outboxRelay.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { OutboxStatus } from './types/outbox.js';
|
|
2
2
|
export interface OutboxRelayOptions {
|
|
3
|
-
/** Unique per process/pod — identifies the lease holder so
|
|
3
|
+
/** Unique per process/pod — identifies the lease holder, so `stop` releases only its own rows. */
|
|
4
4
|
instanceId: string;
|
|
5
5
|
/** Idle poll interval when the outbox is empty (ms). Default 500. */
|
|
6
6
|
pollIntervalMs?: number;
|
|
7
7
|
/** Max rows claimed+dispatched per tick. Default 20. */
|
|
8
8
|
batchSize?: number;
|
|
9
|
-
/** How long a
|
|
9
|
+
/** How long a claimed row stays hidden from other claims (ms). Default 30_000. */
|
|
10
10
|
leaseMs?: number;
|
|
11
11
|
/** Attempts before a row is dead-lettered to `failed`. Default 10. */
|
|
12
12
|
maxAttempts?: number;
|
|
@@ -14,7 +14,7 @@ export interface OutboxRelayOptions {
|
|
|
14
14
|
backoffMs?: (attempt: number) => number;
|
|
15
15
|
/** How long `done` rows are kept (ms). Default 7 days. `Infinity` keeps them forever. */
|
|
16
16
|
keepDoneMs?: number;
|
|
17
|
-
/** How long dead-lettered (`failed`) rows are kept (ms). Default `Infinity
|
|
17
|
+
/** How long dead-lettered (`failed`) rows are kept for inspection or replay (ms). Default 90 days. `Infinity` keeps them forever. */
|
|
18
18
|
keepFailedMs?: number;
|
|
19
19
|
/** Start the background poll loop immediately (default true). Set false to drive `tick`/`drain` manually (tests). */
|
|
20
20
|
autoStart?: boolean;
|
|
@@ -28,33 +28,24 @@ export interface OutboxRelay {
|
|
|
28
28
|
drain: () => Promise<number>;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* Due rows
|
|
32
|
-
* A row for any other collection waits for a relay
|
|
33
|
-
* being marked done with nobody notified. Exported for the
|
|
31
|
+
* Due rows for collections this process has a service for. A claimed row whose lease ran out is
|
|
32
|
+
* due again, so it needs no condition of its own. A row for any other collection waits for a relay
|
|
33
|
+
* that has its listeners, instead of being marked done with nobody notified. Exported for the
|
|
34
|
+
* index test, not part of the public barrel.
|
|
34
35
|
*/
|
|
35
36
|
export declare const claimFilter: (now: Date, collectionNames: string[]) => {
|
|
36
37
|
collectionName: {
|
|
37
38
|
$in: string[];
|
|
38
39
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
};
|
|
44
|
-
leasedUntil?: undefined;
|
|
45
|
-
} | {
|
|
46
|
-
status: OutboxStatus;
|
|
47
|
-
leasedUntil: {
|
|
48
|
-
$lt: Date;
|
|
49
|
-
};
|
|
50
|
-
nextAttemptAt?: undefined;
|
|
51
|
-
})[];
|
|
40
|
+
status: OutboxStatus;
|
|
41
|
+
nextAttemptAt: {
|
|
42
|
+
$lte: Date;
|
|
43
|
+
};
|
|
52
44
|
};
|
|
53
45
|
/**
|
|
54
|
-
* Longest-due first. nextAttemptAt (equal to createdOn for a fresh row)
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* per claimed row.
|
|
46
|
+
* Longest-due first. nextAttemptAt (equal to createdOn for a fresh row) follows collectionName in
|
|
47
|
+
* CLAIM_INDEX, so MongoDB reads each collection's due rows in order and merges them. Sorting by
|
|
48
|
+
* createdOn instead read and sorted every due row in memory, once per claimed row.
|
|
58
49
|
*/
|
|
59
50
|
export declare const CLAIM_SORT: {
|
|
60
51
|
readonly nextAttemptAt: 1;
|
|
@@ -63,9 +54,10 @@ export declare const CLAIM_SORT: {
|
|
|
63
54
|
* Drains the transactional outbox and dispatches each event to the in-process
|
|
64
55
|
* listeners registered on its source collection's bus.
|
|
65
56
|
*
|
|
66
|
-
* Restart-safe and multi-pod-safe:
|
|
67
|
-
*
|
|
68
|
-
* at-least-once (consumers must
|
|
57
|
+
* Restart-safe and multi-pod-safe: a claim is an atomic findOneAndUpdate that
|
|
58
|
+
* pushes the row's nextAttemptAt past a lease, so a row whose holder died is
|
|
59
|
+
* due again once the lease runs out. Delivery is at-least-once (consumers must
|
|
60
|
+
* be idempotent).
|
|
69
61
|
*/
|
|
70
62
|
export declare const startOutboxRelay: (options: OutboxRelayOptions) => OutboxRelay;
|
|
71
63
|
//# sourceMappingURL=outboxRelay.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outboxRelay.d.ts","sourceRoot":"","sources":["../src/outboxRelay.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"outboxRelay.d.ts","sourceRoot":"","sources":["../src/outboxRelay.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAkB,MAAM,mBAAmB,CAAC;AASjE,MAAM,WAAW,kBAAkB;IACjC,kGAAkG;IAClG,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qIAAqI;IACrI,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qHAAqH;IACrH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,6FAA6F;IAC7F,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,6EAA6E;IAC7E,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5B,kGAAkG;IAClG,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9B;AASD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,IAAI,EAAE,iBAAiB,MAAM,EAAE;;;;;;;;CAI9D,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,UAAU;;CAAgC,CAAC;AAWxD;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,kBAAkB,KAAG,WA8L9D,CAAC"}
|
package/dist/outboxRelay.js
CHANGED
|
@@ -1,103 +1,110 @@
|
|
|
1
|
-
|
|
2
|
-
import { getOutboxCollection, getRegisteredCollections, getServiceBus } from './db.js';
|
|
1
|
+
import { dispatchEvent, getOutboxCollection, getRegisteredCollections } from './db.js';
|
|
3
2
|
import { OutboxStatus } from './types/outbox.js';
|
|
3
|
+
// Looked up on each call, so a test can spy on console.
|
|
4
4
|
const logger = {
|
|
5
|
-
info: console.info,
|
|
6
|
-
warn: console.warn,
|
|
7
|
-
error: console.error,
|
|
5
|
+
info: (...args) => console.info(...args),
|
|
6
|
+
warn: (...args) => console.warn(...args),
|
|
7
|
+
error: (...args) => console.error(...args),
|
|
8
8
|
};
|
|
9
9
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
10
|
const defaultBackoff = (attempt) => Math.min(30000, 1000 * 2 ** Math.max(0, attempt - 1));
|
|
11
11
|
/** Rows past their retention are removed by the TTL index on `expireAt`. `null` keeps a row forever. */
|
|
12
12
|
const expiryAfter = (keepMs) => (Number.isFinite(keepMs) ? new Date(Date.now() + keepMs) : null);
|
|
13
13
|
/**
|
|
14
|
-
* Due rows
|
|
15
|
-
* A row for any other collection waits for a relay
|
|
16
|
-
* being marked done with nobody notified. Exported for the
|
|
14
|
+
* Due rows for collections this process has a service for. A claimed row whose lease ran out is
|
|
15
|
+
* due again, so it needs no condition of its own. A row for any other collection waits for a relay
|
|
16
|
+
* that has its listeners, instead of being marked done with nobody notified. Exported for the
|
|
17
|
+
* index test, not part of the public barrel.
|
|
17
18
|
*/
|
|
18
19
|
export const claimFilter = (now, collectionNames) => ({
|
|
19
20
|
collectionName: { $in: collectionNames },
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
{ status: OutboxStatus.Processing, leasedUntil: { $lt: now } },
|
|
23
|
-
],
|
|
21
|
+
status: OutboxStatus.Pending,
|
|
22
|
+
nextAttemptAt: { $lte: now },
|
|
24
23
|
});
|
|
25
24
|
/**
|
|
26
|
-
* Longest-due first. nextAttemptAt (equal to createdOn for a fresh row)
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* per claimed row.
|
|
25
|
+
* Longest-due first. nextAttemptAt (equal to createdOn for a fresh row) follows collectionName in
|
|
26
|
+
* CLAIM_INDEX, so MongoDB reads each collection's due rows in order and merges them. Sorting by
|
|
27
|
+
* createdOn instead read and sorted every due row in memory, once per claimed row.
|
|
30
28
|
*/
|
|
31
29
|
export const CLAIM_SORT = { nextAttemptAt: 1 };
|
|
30
|
+
/**
|
|
31
|
+
* collectionName before nextAttemptAt: rows of collections no relay serves are the oldest due
|
|
32
|
+
* ones, and with nextAttemptAt first every claim read past all of them.
|
|
33
|
+
*/
|
|
34
|
+
const CLAIM_INDEX = { status: 1, collectionName: 1, nextAttemptAt: 1 };
|
|
35
|
+
/** A due row of a collection some relay serves is claimed within seconds, so this is long. */
|
|
36
|
+
const STUCK_AFTER_MS = 60 * 60 * 1000;
|
|
32
37
|
/**
|
|
33
38
|
* Drains the transactional outbox and dispatches each event to the in-process
|
|
34
39
|
* listeners registered on its source collection's bus.
|
|
35
40
|
*
|
|
36
|
-
* Restart-safe and multi-pod-safe:
|
|
37
|
-
*
|
|
38
|
-
* at-least-once (consumers must
|
|
41
|
+
* Restart-safe and multi-pod-safe: a claim is an atomic findOneAndUpdate that
|
|
42
|
+
* pushes the row's nextAttemptAt past a lease, so a row whose holder died is
|
|
43
|
+
* due again once the lease runs out. Delivery is at-least-once (consumers must
|
|
44
|
+
* be idempotent).
|
|
39
45
|
*/
|
|
40
46
|
export const startOutboxRelay = (options) => {
|
|
41
|
-
const { instanceId, pollIntervalMs = 500, batchSize = 20, leaseMs = 30000, maxAttempts = 10, backoffMs = defaultBackoff, keepDoneMs = 7 * 24 * 60 * 60 * 1000, keepFailedMs =
|
|
47
|
+
const { instanceId, pollIntervalMs = 500, batchSize = 20, leaseMs = 30000, maxAttempts = 10, backoffMs = defaultBackoff, keepDoneMs = 7 * 24 * 60 * 60 * 1000, keepFailedMs = 90 * 24 * 60 * 60 * 1000, autoStart = true, } = options;
|
|
42
48
|
let running = true;
|
|
43
49
|
let loopPromise = null;
|
|
44
|
-
let
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
let setUpDone = null;
|
|
51
|
+
/**
|
|
52
|
+
* Rows never expire while pending, so a collection whose rows no relay claims (a one-off script
|
|
53
|
+
* writing with events on, a service missing from the relay process) would grow unnoticed.
|
|
54
|
+
*/
|
|
55
|
+
const warnAboutStuckRows = async () => {
|
|
56
|
+
const stuck = await getOutboxCollection()
|
|
57
|
+
.aggregate([
|
|
58
|
+
{
|
|
59
|
+
$match: {
|
|
60
|
+
status: OutboxStatus.Pending,
|
|
61
|
+
collectionName: { $nin: getRegisteredCollections() },
|
|
62
|
+
nextAttemptAt: { $lt: new Date(Date.now() - STUCK_AFTER_MS) },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{ $group: { _id: '$collectionName', count: { $sum: 1 } } },
|
|
66
|
+
])
|
|
67
|
+
.toArray();
|
|
68
|
+
for (const { _id: collectionName, count } of stuck) {
|
|
69
|
+
logger.warn(`Outbox has ${count} rows for '${collectionName}' that have been due for over an hour, and this relay ` +
|
|
70
|
+
'process has no service for that collection. Unless another relay serves it, they wait forever: ' +
|
|
71
|
+
'create its service in a relay process, or write to it through a service with emitOutboxEvents: false.');
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
/** Indexes, and the stuck-row check, before the first claim. */
|
|
75
|
+
const setUpOnce = () => {
|
|
76
|
+
if (!setUpDone) {
|
|
47
77
|
const col = getOutboxCollection();
|
|
48
|
-
|
|
49
|
-
await col.createIndex(
|
|
78
|
+
setUpDone = (async () => {
|
|
79
|
+
await col.createIndex(CLAIM_INDEX);
|
|
50
80
|
// Retention is per row: markDone/markFailure set expireAt, so changing keepDoneMs
|
|
51
81
|
// needs no index change (createIndex can't alter expireAfterSeconds of an existing index).
|
|
52
82
|
await col.createIndex({ expireAt: 1 }, { expireAfterSeconds: 0 });
|
|
53
|
-
|
|
54
|
-
await col.dropIndex('processedOn_1').catch((err) => {
|
|
55
|
-
if (err?.codeName !== 'IndexNotFound')
|
|
56
|
-
throw err;
|
|
57
|
-
});
|
|
83
|
+
await warnAboutStuckRows().catch((err) => logger.error('Outbox relay could not check for stuck rows:', err));
|
|
58
84
|
})().catch((err) => {
|
|
59
85
|
// Don't cache a failure: the next tick tries again instead of failing forever.
|
|
60
|
-
|
|
86
|
+
setUpDone = null;
|
|
61
87
|
throw err;
|
|
62
88
|
});
|
|
63
89
|
}
|
|
64
|
-
return
|
|
90
|
+
return setUpDone;
|
|
65
91
|
};
|
|
66
92
|
const claimOne = async () => {
|
|
67
93
|
const col = getOutboxCollection();
|
|
68
94
|
const now = new Date();
|
|
69
95
|
const claimed = await col.findOneAndUpdate(claimFilter(now, getRegisteredCollections()), {
|
|
70
|
-
$set: {
|
|
71
|
-
status: OutboxStatus.Processing,
|
|
72
|
-
leasedBy: instanceId,
|
|
73
|
-
leasedUntil: new Date(now.getTime() + leaseMs),
|
|
74
|
-
},
|
|
96
|
+
$set: { leasedBy: instanceId, nextAttemptAt: new Date(now.getTime() + leaseMs) },
|
|
75
97
|
$inc: { attempts: 1 },
|
|
76
98
|
}, { sort: CLAIM_SORT, returnDocument: 'after' });
|
|
77
99
|
return claimed ?? null;
|
|
78
100
|
};
|
|
79
|
-
const dispatch = async (row) => {
|
|
80
|
-
const bus = getServiceBus(row.collectionName);
|
|
81
|
-
// claimFilter only matches registered collections, so this can't happen. Never mark such a row done.
|
|
82
|
-
if (!bus)
|
|
83
|
-
throw new Error(`No service registered for collection '${row.collectionName}'`);
|
|
84
|
-
const payload = {
|
|
85
|
-
prevDoc: row.prevDoc ?? undefined,
|
|
86
|
-
doc: row.doc,
|
|
87
|
-
meta: row.meta ?? undefined,
|
|
88
|
-
context: row.context ?? undefined,
|
|
89
|
-
eventId: row._id,
|
|
90
|
-
};
|
|
91
|
-
const listeners = bus.listeners(row.op);
|
|
92
|
-
await Promise.all(listeners.map(async (l) => l(payload)));
|
|
93
|
-
};
|
|
94
101
|
/**
|
|
95
102
|
* Writes a claimed row's outcome, but only while this claim still holds it. attempts goes up
|
|
96
103
|
* on every claim, so a holder whose lease ran out (and whose row another instance reclaimed)
|
|
97
104
|
* matches nothing instead of overwriting the newer claim's outcome.
|
|
98
105
|
*/
|
|
99
106
|
const settle = async (row, set) => {
|
|
100
|
-
const { matchedCount } = await getOutboxCollection().updateOne({ _id: row._id, leasedBy: instanceId, attempts: row.attempts }, { $set: { ...set, leasedBy: null
|
|
107
|
+
const { matchedCount } = await getOutboxCollection().updateOne({ _id: row._id, leasedBy: instanceId, attempts: row.attempts }, { $set: { ...set, leasedBy: null } });
|
|
101
108
|
if (matchedCount === 0) {
|
|
102
109
|
logger.warn(`Outbox row ${row._id} (${row.collectionName}/${row.op}) was reclaimed after its lease ran out. ` +
|
|
103
110
|
'Raise leaseMs above your slowest listener.');
|
|
@@ -122,8 +129,21 @@ export const startOutboxRelay = (options) => {
|
|
|
122
129
|
}
|
|
123
130
|
};
|
|
124
131
|
const processRow = async (row) => {
|
|
132
|
+
// markFailure dead-letters at maxAttempts, so only claims whose lease ran out get past it: a
|
|
133
|
+
// listener that crashes the process or outlasts leaseMs. Dispatching again would repeat that.
|
|
134
|
+
if (row.attempts > maxAttempts) {
|
|
135
|
+
await markFailure(row, new Error(`lease of ${leaseMs}ms ran out on earlier attempts; not dispatched again`));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
125
138
|
try {
|
|
126
|
-
await
|
|
139
|
+
await dispatchEvent(row.collectionName, {
|
|
140
|
+
op: row.op,
|
|
141
|
+
eventId: row._id,
|
|
142
|
+
doc: row.doc,
|
|
143
|
+
prevDoc: row.prevDoc,
|
|
144
|
+
meta: row.meta,
|
|
145
|
+
context: row.context,
|
|
146
|
+
});
|
|
127
147
|
await markDone(row);
|
|
128
148
|
}
|
|
129
149
|
catch (err) {
|
|
@@ -131,7 +151,7 @@ export const startOutboxRelay = (options) => {
|
|
|
131
151
|
}
|
|
132
152
|
};
|
|
133
153
|
const tick = async () => {
|
|
134
|
-
await
|
|
154
|
+
await setUpOnce();
|
|
135
155
|
let processed = 0;
|
|
136
156
|
for (let i = 0; i < batchSize; i += 1) {
|
|
137
157
|
const row = await claimOne();
|
|
@@ -171,7 +191,9 @@ export const startOutboxRelay = (options) => {
|
|
|
171
191
|
if (loopPromise)
|
|
172
192
|
await loopPromise;
|
|
173
193
|
try {
|
|
174
|
-
|
|
194
|
+
// A held row's nextAttemptAt is its lease end, still ahead: the condition keeps this off the due backlog.
|
|
195
|
+
const now = new Date();
|
|
196
|
+
await getOutboxCollection().updateMany({ status: OutboxStatus.Pending, nextAttemptAt: { $gt: now }, leasedBy: instanceId }, { $set: { leasedBy: null, nextAttemptAt: now } });
|
|
175
197
|
}
|
|
176
198
|
catch (err) {
|
|
177
199
|
logger.error('Outbox relay failed to release leased rows on stop:', err);
|
package/dist/outboxRelay.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outboxRelay.js","sourceRoot":"","sources":["../src/outboxRelay.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"outboxRelay.js","sourceRoot":"","sources":["../src/outboxRelay.ts"],"names":[],"mappings":"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"}
|
package/dist/types/outbox.d.ts
CHANGED
|
@@ -11,12 +11,24 @@ export declare enum OutboxOp {
|
|
|
11
11
|
Updated = "updated",
|
|
12
12
|
Removed = "removed"
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* A claimed row stays `pending`: the claim pushes `nextAttemptAt` past its lease, so a row whose
|
|
16
|
+
* holder died is due again through the same query that finds new rows.
|
|
17
|
+
*/
|
|
14
18
|
export declare enum OutboxStatus {
|
|
15
19
|
Pending = "pending",
|
|
16
|
-
Processing = "processing",
|
|
17
20
|
Done = "done",
|
|
18
21
|
Failed = "failed"
|
|
19
22
|
}
|
|
23
|
+
/** What `dispatchEvent` delivers, and listeners receive. `eventId` is the outbox row's `_id`. */
|
|
24
|
+
export interface OutboxEvent {
|
|
25
|
+
op: `${OutboxOp}`;
|
|
26
|
+
eventId: string;
|
|
27
|
+
doc: unknown;
|
|
28
|
+
prevDoc?: unknown;
|
|
29
|
+
meta?: Record<string, unknown> | null;
|
|
30
|
+
context?: unknown;
|
|
31
|
+
}
|
|
20
32
|
export interface OutboxRow<T = Record<string, unknown>> {
|
|
21
33
|
_id: string;
|
|
22
34
|
collectionName: string;
|
|
@@ -28,7 +40,6 @@ export interface OutboxRow<T = Record<string, unknown>> {
|
|
|
28
40
|
status: OutboxStatus;
|
|
29
41
|
attempts: number;
|
|
30
42
|
nextAttemptAt: Date;
|
|
31
|
-
leasedUntil?: Date | null;
|
|
32
43
|
leasedBy?: string | null;
|
|
33
44
|
lastError?: string | null;
|
|
34
45
|
createdOn: Date;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/types/outbox.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,oBAAY,QAAQ;IAClB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,OAAO,YAAY;CACpB;AAED,oBAAY,YAAY;IACtB,OAAO,YAAY;IACnB,
|
|
1
|
+
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/types/outbox.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,oBAAY,QAAQ;IAClB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,OAAO,YAAY;CACpB;AAED;;;GAGG;AACH,oBAAY,YAAY;IACtB,OAAO,YAAY;IACnB,IAAI,SAAS;IACb,MAAM,WAAW;CAClB;AAED,iGAAiG;AACjG,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,GAAG,QAAQ,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,EAAE,EAAE,QAAQ,CAAC;IACb,GAAG,EAAE,CAAC,CAAC;IACP,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,IAAI,CAAC;IAChB,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB"}
|
package/dist/types/outbox.js
CHANGED
|
@@ -12,10 +12,13 @@ export var OutboxOp;
|
|
|
12
12
|
OutboxOp["Updated"] = "updated";
|
|
13
13
|
OutboxOp["Removed"] = "removed";
|
|
14
14
|
})(OutboxOp || (OutboxOp = {}));
|
|
15
|
+
/**
|
|
16
|
+
* A claimed row stays `pending`: the claim pushes `nextAttemptAt` past its lease, so a row whose
|
|
17
|
+
* holder died is due again through the same query that finds new rows.
|
|
18
|
+
*/
|
|
15
19
|
export var OutboxStatus;
|
|
16
20
|
(function (OutboxStatus) {
|
|
17
21
|
OutboxStatus["Pending"] = "pending";
|
|
18
|
-
OutboxStatus["Processing"] = "processing";
|
|
19
22
|
OutboxStatus["Done"] = "done";
|
|
20
23
|
OutboxStatus["Failed"] = "failed";
|
|
21
24
|
})(OutboxStatus || (OutboxStatus = {}));
|
package/dist/types/outbox.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/types/outbox.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAE1C,MAAM,CAAN,IAAY,QAIX;AAJD,WAAY,QAAQ;IAClB,+BAAmB,CAAA;IACnB,+BAAmB,CAAA;IACnB,+BAAmB,CAAA;AACrB,CAAC,EAJW,QAAQ,KAAR,QAAQ,QAInB;AAED,MAAM,CAAN,IAAY,
|
|
1
|
+
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/types/outbox.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAE1C,MAAM,CAAN,IAAY,QAIX;AAJD,WAAY,QAAQ;IAClB,+BAAmB,CAAA;IACnB,+BAAmB,CAAA;IACnB,+BAAmB,CAAA;AACrB,CAAC,EAJW,QAAQ,KAAR,QAAQ,QAInB;AAED;;;GAGG;AACH,MAAM,CAAN,IAAY,YAIX;AAJD,WAAY,YAAY;IACtB,mCAAmB,CAAA;IACnB,6BAAa,CAAA;IACb,iCAAiB,CAAA;AACnB,CAAC,EAJW,YAAY,KAAZ,YAAY,QAIvB"}
|