@causa/runtime-google 0.36.0 → 0.37.0
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/spanner/types.d.ts
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
|
+
import type { SpannerReadOnlyTransaction, SpannerReadWriteTransaction } from './entity-manager.js';
|
|
1
2
|
/**
|
|
2
3
|
* A partial Spanner entity instance, where nested objects can also be partial.
|
|
3
4
|
*/
|
|
4
5
|
export type RecursivePartialEntity<T> = T extends Date ? T : T extends object ? Partial<T> | {
|
|
5
6
|
[P in keyof T]?: RecursivePartialEntity<T[P]>;
|
|
6
7
|
} : T;
|
|
8
|
+
/**
|
|
9
|
+
* Option for a function that accepts a Spanner read-only transaction.
|
|
10
|
+
*/
|
|
11
|
+
export type SpannerReadOnlyTransactionOption = {
|
|
12
|
+
/**
|
|
13
|
+
* The transaction to use.
|
|
14
|
+
*/
|
|
15
|
+
readonly transaction?: SpannerReadOnlyTransaction;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Option for a function that accepts a Spanner read-write transaction.
|
|
19
|
+
*/
|
|
20
|
+
export type SpanerReadWriteTransactionOption = {
|
|
21
|
+
/**
|
|
22
|
+
* The transaction to use.
|
|
23
|
+
*/
|
|
24
|
+
readonly transaction?: SpannerReadWriteTransaction;
|
|
25
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { SpannerOutboxEvent } from './event.js';
|
|
2
2
|
export { SpannerOutboxTransactionModule } from './module.js';
|
|
3
3
|
export { SpannerOutboxTransactionRunner } from './runner.js';
|
|
4
|
-
export type { SpannerOutboxTransaction } from './runner.js';
|
|
4
|
+
export type { SpannerOutboxTransaction, SpannerOutboxTransactionOption, } from './runner.js';
|
|
5
5
|
export { SpannerOutboxSender } from './sender.js';
|
|
@@ -8,6 +8,15 @@ import { SpannerOutboxSender } from './sender.js';
|
|
|
8
8
|
* A {@link SpannerTransaction} that uses an {@link OutboxEventTransaction}.
|
|
9
9
|
*/
|
|
10
10
|
export type SpannerOutboxTransaction = SpannerTransaction<OutboxEventTransaction>;
|
|
11
|
+
/**
|
|
12
|
+
* Option for a function that accepts a {@link SpannerOutboxTransaction}.
|
|
13
|
+
*/
|
|
14
|
+
export type SpannerOutboxTransactionOption = {
|
|
15
|
+
/**
|
|
16
|
+
* The transaction to use.
|
|
17
|
+
*/
|
|
18
|
+
readonly transaction?: SpannerOutboxTransaction;
|
|
19
|
+
};
|
|
11
20
|
/**
|
|
12
21
|
* An {@link OutboxTransactionRunner} that uses a {@link SpannerTransaction} to run transactions.
|
|
13
22
|
* Events are stored in a Spanner table before being published.
|
|
@@ -63,9 +63,15 @@ export declare class SpannerOutboxSender extends OutboxEventSender {
|
|
|
63
63
|
*/
|
|
64
64
|
readonly index: string | undefined;
|
|
65
65
|
/**
|
|
66
|
-
* The SQL query used to fetch events from the outbox.
|
|
66
|
+
* The SQL query used to fetch (non-leased) events from the outbox.
|
|
67
|
+
* This should be run in a read-only transaction to avoid table-wide locking.
|
|
67
68
|
*/
|
|
68
69
|
readonly fetchEventsSql: string;
|
|
70
|
+
/**
|
|
71
|
+
* The SQL query used to acquire a lease on events in the outbox.
|
|
72
|
+
* This is based on event IDs, that are checked again to have a null lease expiration.
|
|
73
|
+
*/
|
|
74
|
+
readonly acquireLeaseSql: string;
|
|
69
75
|
/**
|
|
70
76
|
* The SQL query used to update events in the outbox after they have been successfully published.
|
|
71
77
|
*/
|
|
@@ -89,7 +95,7 @@ export declare class SpannerOutboxSender extends OutboxEventSender {
|
|
|
89
95
|
*
|
|
90
96
|
* @returns The SQL queries used to fetch and update events in the outbox.
|
|
91
97
|
*/
|
|
92
|
-
protected buildSql(): Pick<SpannerOutboxSender, 'fetchEventsSql' | 'successfulUpdateSql' | 'failedUpdateSql'>;
|
|
98
|
+
protected buildSql(): Pick<SpannerOutboxSender, 'fetchEventsSql' | 'acquireLeaseSql' | 'successfulUpdateSql' | 'failedUpdateSql'>;
|
|
93
99
|
protected fetchEvents(): Promise<OutboxEvent[]>;
|
|
94
100
|
protected updateOutbox(result: OutboxEventPublishResult): Promise<void>;
|
|
95
101
|
}
|
|
@@ -33,9 +33,15 @@ export class SpannerOutboxSender extends OutboxEventSender {
|
|
|
33
33
|
*/
|
|
34
34
|
index;
|
|
35
35
|
/**
|
|
36
|
-
* The SQL query used to fetch events from the outbox.
|
|
36
|
+
* The SQL query used to fetch (non-leased) events from the outbox.
|
|
37
|
+
* This should be run in a read-only transaction to avoid table-wide locking.
|
|
37
38
|
*/
|
|
38
39
|
fetchEventsSql;
|
|
40
|
+
/**
|
|
41
|
+
* The SQL query used to acquire a lease on events in the outbox.
|
|
42
|
+
* This is based on event IDs, that are checked again to have a null lease expiration.
|
|
43
|
+
*/
|
|
44
|
+
acquireLeaseSql;
|
|
39
45
|
/**
|
|
40
46
|
* The SQL query used to update events in the outbox after they have been successfully published.
|
|
41
47
|
*/
|
|
@@ -64,6 +70,7 @@ export class SpannerOutboxSender extends OutboxEventSender {
|
|
|
64
70
|
this.index = options.index;
|
|
65
71
|
({
|
|
66
72
|
fetchEventsSql: this.fetchEventsSql,
|
|
73
|
+
acquireLeaseSql: this.acquireLeaseSql,
|
|
67
74
|
successfulUpdateSql: this.successfulUpdateSql,
|
|
68
75
|
failedUpdateSql: this.failedUpdateSql,
|
|
69
76
|
} = this.buildSql());
|
|
@@ -76,27 +83,32 @@ export class SpannerOutboxSender extends OutboxEventSender {
|
|
|
76
83
|
buildSql() {
|
|
77
84
|
const table = this.entityManager.sqlTableName(this.outboxEventType);
|
|
78
85
|
const tableWithIndex = this.entityManager.sqlTableName(this.outboxEventType, { index: this.index });
|
|
79
|
-
|
|
86
|
+
const noLeaseFilter = `\`${this.leaseExpirationColumn}\` IS NULL OR \`${this.leaseExpirationColumn}\` < @currentTime`;
|
|
87
|
+
let fetchFilter = noLeaseFilter;
|
|
80
88
|
if (this.sharding) {
|
|
81
89
|
const { column, count } = this.sharding;
|
|
82
|
-
|
|
90
|
+
fetchFilter = `\`${column}\` BETWEEN 0 AND ${count - 1} AND (${fetchFilter})`;
|
|
83
91
|
}
|
|
84
92
|
const fetchEventsSql = `
|
|
93
|
+
SELECT
|
|
94
|
+
\`${this.idColumn}\` AS id,
|
|
95
|
+
FROM
|
|
96
|
+
${tableWithIndex}
|
|
97
|
+
WHERE
|
|
98
|
+
${fetchFilter}
|
|
99
|
+
LIMIT
|
|
100
|
+
@batchSize
|
|
101
|
+
`;
|
|
102
|
+
// This will not be run in the same transaction as `fetchEventsSql`. The `noLeaseFilter` is re-applied to avoid
|
|
103
|
+
// acquiring a lease on outbox events that have since been leased by another process.
|
|
104
|
+
const acquireLeaseSql = `
|
|
85
105
|
UPDATE
|
|
86
106
|
${table}
|
|
87
107
|
SET
|
|
88
108
|
\`${this.leaseExpirationColumn}\` = @leaseExpiration
|
|
89
109
|
WHERE
|
|
90
|
-
\`${this.idColumn}\` IN (
|
|
91
|
-
|
|
92
|
-
\`${this.idColumn}\`
|
|
93
|
-
FROM
|
|
94
|
-
${tableWithIndex}
|
|
95
|
-
WHERE
|
|
96
|
-
${filter}
|
|
97
|
-
LIMIT
|
|
98
|
-
@batchSize
|
|
99
|
-
)
|
|
110
|
+
\`${this.idColumn}\` IN UNNEST(@ids)
|
|
111
|
+
AND (${noLeaseFilter})
|
|
100
112
|
THEN RETURN
|
|
101
113
|
${this.entityManager.sqlColumns(this.outboxEventType)}`;
|
|
102
114
|
const successfulUpdateSql = `
|
|
@@ -111,18 +123,29 @@ export class SpannerOutboxSender extends OutboxEventSender {
|
|
|
111
123
|
\`${this.leaseExpirationColumn}\` = NULL
|
|
112
124
|
WHERE
|
|
113
125
|
\`${this.idColumn}\` IN UNNEST(@ids)`;
|
|
114
|
-
return {
|
|
126
|
+
return {
|
|
127
|
+
fetchEventsSql,
|
|
128
|
+
acquireLeaseSql,
|
|
129
|
+
successfulUpdateSql,
|
|
130
|
+
failedUpdateSql,
|
|
131
|
+
};
|
|
115
132
|
}
|
|
116
133
|
async fetchEvents() {
|
|
134
|
+
// Event IDs are first acquired in an (implicit) read-only transaction, to avoid a lock on the entire table.
|
|
135
|
+
const currentTime = new Date();
|
|
136
|
+
const eventIds = await this.entityManager.query({
|
|
137
|
+
sql: this.fetchEventsSql,
|
|
138
|
+
params: { currentTime, batchSize: this.batchSize },
|
|
139
|
+
});
|
|
140
|
+
if (eventIds.length === 0) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
const ids = eventIds.map(({ id }) => id);
|
|
117
144
|
return await this.entityManager.transaction(async (transaction) => {
|
|
118
145
|
const currentTime = new Date();
|
|
119
146
|
const leaseExpiration = new Date(currentTime.getTime() + this.leaseDuration);
|
|
120
|
-
const params = {
|
|
121
|
-
|
|
122
|
-
currentTime,
|
|
123
|
-
batchSize: this.batchSize,
|
|
124
|
-
};
|
|
125
|
-
return await this.entityManager.query({ transaction, entityType: this.outboxEventType }, { sql: this.fetchEventsSql, params });
|
|
147
|
+
const params = { ids, leaseExpiration, currentTime };
|
|
148
|
+
return await this.entityManager.query({ transaction, entityType: this.outboxEventType }, { sql: this.acquireLeaseSql, params });
|
|
126
149
|
});
|
|
127
150
|
}
|
|
128
151
|
async updateOutbox(result) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@causa/runtime-google",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "An extension to the Causa runtime SDK (`@causa/runtime`), providing Google-specific features.",
|
|
5
5
|
"repository": "github:causa-io/runtime-typescript-google",
|
|
6
6
|
"license": "ISC",
|