@eventualize/dynamodb-storage-adapter 1.0.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/DynamoDbClient.d.ts +3 -0
- package/dist/DynamoDbClient.js +30 -0
- package/dist/DynamoDbClient.js.map +1 -0
- package/dist/EvDBDynamoDBAdmin.d.ts +17 -0
- package/dist/EvDBDynamoDBAdmin.js +101 -0
- package/dist/EvDBDynamoDBAdmin.js.map +1 -0
- package/dist/EvDbDynamoDbStorageAdapter.d.ts +86 -0
- package/dist/EvDbDynamoDbStorageAdapter.js +160 -0
- package/dist/EvDbDynamoDbStorageAdapter.js.map +1 -0
- package/dist/EvDbDynamoDbStorageAdapterQueries.d.ts +39 -0
- package/dist/EvDbDynamoDbStorageAdapterQueries.js +159 -0
- package/dist/EvDbDynamoDbStorageAdapterQueries.js.map +1 -0
- package/models/events-table-schema.json +50 -0
- package/models/messages-table-schema.json +24 -0
- package/models/snapshots-table-schema.json +24 -0
- package/package.json +29 -0
- package/src/DynamoDbClient.ts +32 -0
- package/src/EvDBDynamoDBAdmin.ts +115 -0
- package/src/EvDbDynamoDbStorageAdapter.ts +250 -0
- package/src/EvDbDynamoDbStorageAdapterQueries.ts +210 -0
- package/src/dbModel.json +215 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/dynamo-client.ts
|
|
2
|
+
import { DynamoDBClient, ListTablesCommand } from "@aws-sdk/client-dynamodb";
|
|
3
|
+
export async function listTables(client) {
|
|
4
|
+
try {
|
|
5
|
+
const command = new ListTablesCommand({});
|
|
6
|
+
const response = await client.send(command);
|
|
7
|
+
console.log("Tables in DynamoDB Local:", response.TableNames);
|
|
8
|
+
return response.TableNames;
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
console.error("Error listing tables:", error);
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function createDynamoDBClient() {
|
|
16
|
+
const endpoint = process.env.DYNAMODB_URL;
|
|
17
|
+
const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
|
|
18
|
+
const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
19
|
+
const region = process.env.AWS_REGION || 'us-east-1';
|
|
20
|
+
if (!endpoint || !accessKeyId || !secretAccessKey) {
|
|
21
|
+
const envVars = { endpoint, accessKeyId, secretAccessKey };
|
|
22
|
+
throw new Error("AWS credentials are not set in environment variables: " + JSON.stringify(envVars));
|
|
23
|
+
}
|
|
24
|
+
const config = {};
|
|
25
|
+
config.endpoint = endpoint;
|
|
26
|
+
config.credentials = { accessKeyId, secretAccessKey };
|
|
27
|
+
config.region = region;
|
|
28
|
+
return new DynamoDBClient(config);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=DynamoDbClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DynamoDbClient.js","sourceRoot":"","sources":["../src/DynamoDbClient.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,OAAO,EAAE,cAAc,EAAwB,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAEnG,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAsB;IACnD,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,EAAE,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC9D,OAAO,QAAQ,CAAC,UAAU,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;QAC9C,MAAM,KAAK,CAAC;IAChB,CAAC;AACL,CAAC;AAED,MAAM,UAAU,oBAAoB;IAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAC1C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAClD,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW,CAAC;IAErD,IAAI,CAAC,QAAQ,IAAI,CAAC,WAAW,IAAI,CAAC,eAAe,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,wDAAwD,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACxG,CAAC;IAED,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,MAAM,CAAC,WAAW,GAAG,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;IACtD,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import IEvDbStorageAdmin from "@eventualize/types/IEvDbStorageAdmin";
|
|
2
|
+
export default class EvDbDynamoDbAdmin implements IEvDbStorageAdmin {
|
|
3
|
+
private dynamoDbClient;
|
|
4
|
+
constructor();
|
|
5
|
+
private extractKeys;
|
|
6
|
+
/**
|
|
7
|
+
* Scans a table for all items and deletes them in batches of 25.
|
|
8
|
+
* @param tableName The name of the table to clear.
|
|
9
|
+
*/
|
|
10
|
+
private clearTableItems;
|
|
11
|
+
createEnvironmentAsync(): Promise<void>;
|
|
12
|
+
destroyEnvironmentAsync(): Promise<void>;
|
|
13
|
+
clearEnvironmentAsync(): Promise<void>;
|
|
14
|
+
dispose?(): void;
|
|
15
|
+
disposeAsync(): Promise<void>;
|
|
16
|
+
close(): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { BatchWriteItemCommand, ScanCommand } from "@aws-sdk/client-dynamodb";
|
|
2
|
+
import { createDynamoDBClient } from "./DynamoDbClient.js";
|
|
3
|
+
export default class EvDbDynamoDbAdmin {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.dynamoDbClient = createDynamoDBClient();
|
|
6
|
+
}
|
|
7
|
+
;
|
|
8
|
+
// Helper function to extract keys in raw DynamoDB format
|
|
9
|
+
extractKeys(tableName, items) {
|
|
10
|
+
return items.map(item => {
|
|
11
|
+
const deleteRequest = {
|
|
12
|
+
DeleteRequest: {
|
|
13
|
+
Key: {} // Must build the Key object manually
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
if (tableName === "events") {
|
|
17
|
+
deleteRequest.DeleteRequest.Key["stream_address"] = item["stream_address"];
|
|
18
|
+
deleteRequest.DeleteRequest.Key["offset"] = item["offset"];
|
|
19
|
+
}
|
|
20
|
+
else if (tableName === "snapshots") {
|
|
21
|
+
deleteRequest.DeleteRequest.Key["view_address"] = item["view_address"];
|
|
22
|
+
deleteRequest.DeleteRequest.Key["offset"] = item["offset"];
|
|
23
|
+
}
|
|
24
|
+
else if (tableName === "messages") {
|
|
25
|
+
// Assuming the schema typo was fixed to message_address
|
|
26
|
+
deleteRequest.DeleteRequest.Key["message_address"] = item["message_address"];
|
|
27
|
+
deleteRequest.DeleteRequest.Key["captured_at"] = item["captured_at"];
|
|
28
|
+
}
|
|
29
|
+
return deleteRequest;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Scans a table for all items and deletes them in batches of 25.
|
|
34
|
+
* @param tableName The name of the table to clear.
|
|
35
|
+
*/
|
|
36
|
+
async clearTableItems(tableName) {
|
|
37
|
+
let items;
|
|
38
|
+
let exclusiveStartKey = undefined;
|
|
39
|
+
const BATCH_SIZE = 25;
|
|
40
|
+
do {
|
|
41
|
+
// Define ProjectionExpression and ExpressionAttributeNames dynamically
|
|
42
|
+
let projectionExpression;
|
|
43
|
+
let expressionAttributeNames = undefined;
|
|
44
|
+
if (tableName === "events") {
|
|
45
|
+
projectionExpression = "stream_address, #o";
|
|
46
|
+
expressionAttributeNames = { "#o": "offset" };
|
|
47
|
+
}
|
|
48
|
+
else if (tableName === "snapshots") {
|
|
49
|
+
projectionExpression = "view_address, #o";
|
|
50
|
+
expressionAttributeNames = { "#o": "offset" };
|
|
51
|
+
}
|
|
52
|
+
else { // "messages" table
|
|
53
|
+
projectionExpression = "message_address, captured_at";
|
|
54
|
+
// No offset in the projection for messages table, so no #o alias is needed
|
|
55
|
+
}
|
|
56
|
+
const scanCommand = new ScanCommand({
|
|
57
|
+
TableName: tableName,
|
|
58
|
+
ProjectionExpression: projectionExpression,
|
|
59
|
+
ExpressionAttributeNames: expressionAttributeNames, // Pass the dynamic object
|
|
60
|
+
Limit: BATCH_SIZE,
|
|
61
|
+
ExclusiveStartKey: exclusiveStartKey,
|
|
62
|
+
});
|
|
63
|
+
// ... (rest of the function for scanning and batch writing remains the same) ...
|
|
64
|
+
const scanResult = await this.dynamoDbClient.send(scanCommand);
|
|
65
|
+
items = scanResult.Items;
|
|
66
|
+
if (items && items.length > 0) {
|
|
67
|
+
const deleteRequests = this.extractKeys(tableName, items);
|
|
68
|
+
const batchWriteCommand = new BatchWriteItemCommand({
|
|
69
|
+
RequestItems: {
|
|
70
|
+
[tableName]: deleteRequests
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
await this.dynamoDbClient.send(batchWriteCommand);
|
|
74
|
+
console.log(`Deleted ${items.length} items from ${tableName}.`);
|
|
75
|
+
}
|
|
76
|
+
exclusiveStartKey = scanResult.LastEvaluatedKey;
|
|
77
|
+
} while (exclusiveStartKey);
|
|
78
|
+
console.log(`Finished item deletion for table: ${tableName}`);
|
|
79
|
+
}
|
|
80
|
+
createEnvironmentAsync() {
|
|
81
|
+
throw new Error("Method not implemented.");
|
|
82
|
+
}
|
|
83
|
+
destroyEnvironmentAsync() {
|
|
84
|
+
throw new Error("Method not implemented.");
|
|
85
|
+
}
|
|
86
|
+
async clearEnvironmentAsync() {
|
|
87
|
+
await this.clearTableItems("events");
|
|
88
|
+
await this.clearTableItems("snapshots");
|
|
89
|
+
await this.clearTableItems("messages");
|
|
90
|
+
}
|
|
91
|
+
dispose() {
|
|
92
|
+
throw new Error("Method not implemented.");
|
|
93
|
+
}
|
|
94
|
+
disposeAsync() {
|
|
95
|
+
throw new Error("Method not implemented.");
|
|
96
|
+
}
|
|
97
|
+
async close() {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=EvDBDynamoDBAdmin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EvDBDynamoDBAdmin.js","sourceRoot":"","sources":["../src/EvDBDynamoDBAdmin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { IEvDbPayloadData } from '@eventualize/types/IEvDbEventPayload';
|
|
2
|
+
import IEvDbEventMetadata from '@eventualize/types/IEvDbEventMetadata';
|
|
3
|
+
import EvDbStreamCursor from '@eventualize/types/EvDbStreamCursor';
|
|
4
|
+
import EvDbMessage from '@eventualize/types/EvDbMessage';
|
|
5
|
+
import IEvDbStorageSnapshotAdapter from '@eventualize/types/IEvDbStorageSnapshotAdapter';
|
|
6
|
+
import IEvDbStorageStreamAdapter from '@eventualize/types/IEvDbStorageStreamAdapter';
|
|
7
|
+
import EvDbStreamAddress from '@eventualize/types/EvDbStreamAddress';
|
|
8
|
+
import EvDbViewAddress from '@eventualize/types/EvDbViewAddress';
|
|
9
|
+
import { EvDbStoredSnapshotResultRaw } from '@eventualize/types/EvDbStoredSnapshotResult';
|
|
10
|
+
import { EvDbStoredSnapshotData } from '@eventualize/types/EvDbStoredSnapshotData';
|
|
11
|
+
import EvDbEvent from '@eventualize/types/EvDbEvent';
|
|
12
|
+
import StreamStoreAffected from '@eventualize/types/StreamStoreAffected';
|
|
13
|
+
import EvDbContinuousFetchOptions from '@eventualize/types/EvDbContinuousFetchOptions';
|
|
14
|
+
import EvDbMessageFilter from '@eventualize/types/EvDbMessageFilter';
|
|
15
|
+
import { EvDbShardName } from '@eventualize/types/primitiveTypes';
|
|
16
|
+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
17
|
+
export interface EvDbEventRecord extends IEvDbEventMetadata {
|
|
18
|
+
id: string;
|
|
19
|
+
payload: IEvDbPayloadData;
|
|
20
|
+
}
|
|
21
|
+
export interface EvDbSnapshotRecord {
|
|
22
|
+
id: string;
|
|
23
|
+
streamType: string;
|
|
24
|
+
streamId: string;
|
|
25
|
+
viewName: string;
|
|
26
|
+
offset: bigint;
|
|
27
|
+
state: IEvDbPayloadData;
|
|
28
|
+
}
|
|
29
|
+
export interface IEvDbOutboxTransformer {
|
|
30
|
+
transform(message: EvDbMessage): EvDbMessage;
|
|
31
|
+
}
|
|
32
|
+
export interface EvDbStorageContext {
|
|
33
|
+
schema?: string;
|
|
34
|
+
shortId: string;
|
|
35
|
+
id: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Prisma-based storage adapter for EvDb
|
|
39
|
+
* Replaces SQL Server-specific adapter with database-agnostic Prisma implementation
|
|
40
|
+
*/
|
|
41
|
+
export default class EvDbDynamoDbStorageAdapter implements IEvDbStorageSnapshotAdapter, IEvDbStorageStreamAdapter {
|
|
42
|
+
private dynamoDbClient;
|
|
43
|
+
constructor(dynamoDbClient?: DynamoDBClient);
|
|
44
|
+
getFromOutbox(filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null): Promise<AsyncIterable<EvDbMessage>>;
|
|
45
|
+
getFromOutboxAsync(shard: EvDbShardName, filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null, cancellation?: AbortSignal): AsyncIterable<EvDbMessage>;
|
|
46
|
+
getRecordsFromOutboxAsync(filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null, cancellation?: AbortSignal): AsyncIterable<EvDbMessage>;
|
|
47
|
+
getRecordsFromOutboxAsync(shard: EvDbShardName, filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null, cancellation?: AbortSignal): AsyncIterable<EvDbMessage>;
|
|
48
|
+
subscribeToMessageAsync(handler: (message: EvDbMessage) => Promise<void>, filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null): Promise<void>;
|
|
49
|
+
subscribeToMessageAsync(handler: (message: EvDbMessage) => Promise<void>, shard: EvDbShardName, filter: EvDbMessageFilter, options?: EvDbContinuousFetchOptions | null): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Store stream events in a transaction
|
|
52
|
+
*/
|
|
53
|
+
storeStreamAsync(events: ReadonlyArray<EvDbEvent>, messages: ReadonlyArray<EvDbMessage>): Promise<StreamStoreAffected>;
|
|
54
|
+
/**
|
|
55
|
+
* Store outbox messages in a transaction
|
|
56
|
+
*/
|
|
57
|
+
storeOutboxMessagesAsync(shardName: EvDbShardName, records: EvDbMessage[]): Promise<number>;
|
|
58
|
+
/**
|
|
59
|
+
* Get the last offset for a stream
|
|
60
|
+
*/
|
|
61
|
+
getLastOffsetAsync(streamAddress: EvDbStreamAddress): Promise<number>;
|
|
62
|
+
/**
|
|
63
|
+
* Get events for a stream since a specific offset
|
|
64
|
+
*/
|
|
65
|
+
getEventsAsync(streamCursor: EvDbStreamCursor, pageSize?: number): AsyncGenerator<EvDbEvent, void, undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* Get snapshot for a stream view
|
|
68
|
+
*/
|
|
69
|
+
getSnapshotAsync(viewAddress: EvDbViewAddress): Promise<EvDbStoredSnapshotResultRaw>;
|
|
70
|
+
/**
|
|
71
|
+
* Save a snapshot
|
|
72
|
+
*/
|
|
73
|
+
storeSnapshotAsync(record: EvDbStoredSnapshotData): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Check if an exception is an optimistic concurrency conflict
|
|
76
|
+
*/
|
|
77
|
+
private isOccException;
|
|
78
|
+
/**
|
|
79
|
+
* Get table name for shard
|
|
80
|
+
*/
|
|
81
|
+
private getTableNameForShard;
|
|
82
|
+
/**
|
|
83
|
+
* Close the database connection
|
|
84
|
+
*/
|
|
85
|
+
close(): Promise<void>;
|
|
86
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { unmarshall } from "@aws-sdk/util-dynamodb";
|
|
2
|
+
import EvDbStreamCursor from '@eventualize/types/EvDbStreamCursor';
|
|
3
|
+
import { EvDbStoredSnapshotResultRaw } from '@eventualize/types/EvDbStoredSnapshotResult';
|
|
4
|
+
import StreamStoreAffected from '@eventualize/types/StreamStoreAffected';
|
|
5
|
+
import { createDynamoDBClient, listTables } from './DynamoDbClient.js';
|
|
6
|
+
import QueryProvider, { deserializeStreamAddress, EventRecord } from './EvDbDynamoDbStorageAdapterQueries.js';
|
|
7
|
+
import { TransactionCanceledException, TransactWriteItemsCommand } from '@aws-sdk/client-dynamodb';
|
|
8
|
+
const serializePayload = (payload) => Buffer.from(JSON.stringify(payload), 'utf-8');
|
|
9
|
+
const deserializePayload = (payload) => {
|
|
10
|
+
if (!!payload && typeof payload == 'object') {
|
|
11
|
+
return payload;
|
|
12
|
+
}
|
|
13
|
+
return {};
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Prisma-based storage adapter for EvDb
|
|
17
|
+
* Replaces SQL Server-specific adapter with database-agnostic Prisma implementation
|
|
18
|
+
*/
|
|
19
|
+
export default class EvDbDynamoDbStorageAdapter {
|
|
20
|
+
constructor(dynamoDbClient = createDynamoDBClient()) {
|
|
21
|
+
this.dynamoDbClient = dynamoDbClient;
|
|
22
|
+
}
|
|
23
|
+
getFromOutbox(filter, options) {
|
|
24
|
+
throw new Error('Method not implemented.');
|
|
25
|
+
}
|
|
26
|
+
getFromOutboxAsync(shard, filter, options, cancellation) {
|
|
27
|
+
throw new Error('Method not implemented.');
|
|
28
|
+
}
|
|
29
|
+
getRecordsFromOutboxAsync(shard, filter, options, cancellation) {
|
|
30
|
+
throw new Error('Method not implemented.');
|
|
31
|
+
}
|
|
32
|
+
subscribeToMessageAsync(handler, shard, filter, options) {
|
|
33
|
+
throw new Error('Method not implemented.');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Store stream events in a transaction
|
|
37
|
+
*/
|
|
38
|
+
async storeStreamAsync(events, messages) {
|
|
39
|
+
try {
|
|
40
|
+
await listTables(this.dynamoDbClient);
|
|
41
|
+
const eventsToInsert = events.map((event) => EventRecord.createFromEvent(event));
|
|
42
|
+
const messagesToInsert = messages.map(message => {
|
|
43
|
+
return {
|
|
44
|
+
id: crypto.randomUUID(),
|
|
45
|
+
stream_cursor: message.streamCursor,
|
|
46
|
+
channel: message.channel,
|
|
47
|
+
message_type: message.messageType,
|
|
48
|
+
event_type: message.eventType,
|
|
49
|
+
captured_by: message.capturedBy,
|
|
50
|
+
captured_at: message.capturedAt,
|
|
51
|
+
payload: message.payload,
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
const storeEventsQuery = QueryProvider.saveEvents(eventsToInsert);
|
|
55
|
+
const storeMessagesQuery = QueryProvider.saveMessages(messagesToInsert);
|
|
56
|
+
const transactItems = { TransactItems: [...storeEventsQuery, ...storeMessagesQuery] };
|
|
57
|
+
const command = new TransactWriteItemsCommand(transactItems);
|
|
58
|
+
await this.dynamoDbClient.send(command);
|
|
59
|
+
const numEvents = eventsToInsert.length;
|
|
60
|
+
const numMessages = messagesToInsert
|
|
61
|
+
.reduce((prev, { message_type: t }) => Object.assign(prev, { [t]: (prev[t] ?? 0) + 1 }), {});
|
|
62
|
+
return new StreamStoreAffected(numEvents, new Map(Object.entries(numMessages)));
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (this.isOccException(error)) {
|
|
66
|
+
throw new Error('OPTIMISTIC_CONCURRENCY_VIOLATION');
|
|
67
|
+
}
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Store outbox messages in a transaction
|
|
73
|
+
*/
|
|
74
|
+
async storeOutboxMessagesAsync(shardName, records) {
|
|
75
|
+
throw new Error('Method not implemented.');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get the last offset for a stream
|
|
79
|
+
*/
|
|
80
|
+
async getLastOffsetAsync(streamAddress) {
|
|
81
|
+
const query = QueryProvider.getLastOffset(streamAddress);
|
|
82
|
+
const response = await this.dynamoDbClient.send(query);
|
|
83
|
+
if (!response.Items) {
|
|
84
|
+
return -1;
|
|
85
|
+
}
|
|
86
|
+
return parseInt(response.Items[0]?.offset.N ?? '-1', 10);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get events for a stream since a specific offset
|
|
90
|
+
*/
|
|
91
|
+
async *getEventsAsync(streamCursor, pageSize = 100) {
|
|
92
|
+
let queryCursor = undefined;
|
|
93
|
+
do {
|
|
94
|
+
const getEventsCommand = QueryProvider.getEvents(streamCursor);
|
|
95
|
+
const response = await this.dynamoDbClient.send(getEventsCommand);
|
|
96
|
+
if (response.Items && response.Items.length > 0) {
|
|
97
|
+
for (const item of response.Items) {
|
|
98
|
+
const res = unmarshall(item);
|
|
99
|
+
const streamAddress = deserializeStreamAddress(res.stream_address);
|
|
100
|
+
const r = new EventRecord(crypto.randomUUID(), new EvDbStreamCursor(streamAddress.streamType, streamAddress.streamId, res.offset), res.event_type, res.captured_by, new Date(res.captured_at), res.payload, new Date(res.stored_at));
|
|
101
|
+
yield r.toEvDbEvent();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
queryCursor = response.LastEvaluatedKey;
|
|
105
|
+
} while (queryCursor);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get snapshot for a stream view
|
|
109
|
+
*/
|
|
110
|
+
async getSnapshotAsync(viewAddress) {
|
|
111
|
+
const { streamType, streamId, viewName } = viewAddress;
|
|
112
|
+
try {
|
|
113
|
+
const query = QueryProvider.getSnapshot(viewAddress);
|
|
114
|
+
const response = await this.dynamoDbClient.send(query);
|
|
115
|
+
if (!response.Items) {
|
|
116
|
+
return EvDbStoredSnapshotResultRaw.Empty;
|
|
117
|
+
}
|
|
118
|
+
const snapshot = unmarshall(response.Items[0]);
|
|
119
|
+
return new EvDbStoredSnapshotResultRaw(snapshot.offset, new Date(Number(snapshot.stored_at)), snapshot.state);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Save a snapshot
|
|
127
|
+
*/
|
|
128
|
+
async storeSnapshotAsync(record) {
|
|
129
|
+
try {
|
|
130
|
+
const command = QueryProvider.saveSnapshot(record);
|
|
131
|
+
await this.dynamoDbClient.send(command);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Check if an exception is an optimistic concurrency conflict
|
|
139
|
+
*/
|
|
140
|
+
isOccException(error) {
|
|
141
|
+
if (!(error instanceof TransactionCanceledException)) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return !!error
|
|
145
|
+
.CancellationReasons?.some(({ Code }) => Code === 'ConditionalCheckFailed');
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get table name for shard
|
|
149
|
+
*/
|
|
150
|
+
getTableNameForShard(shardName) {
|
|
151
|
+
throw new Error('Method not implemented.');
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Close the database connection
|
|
155
|
+
*/
|
|
156
|
+
async close() {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=EvDbDynamoDbStorageAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EvDbDynamoDbStorageAdapter.js","sourceRoot":"","sources":["../src/EvDbDynamoDbStorageAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import IEvDbEventPayload, { IEvDbPayloadData } from "@eventualize/types/IEvDbEventPayload";
|
|
2
|
+
import { PutItemCommand, QueryCommand, TransactWriteItem } from "@aws-sdk/client-dynamodb";
|
|
3
|
+
import EvDbStreamCursor from "@eventualize/types/EvDbStreamCursor";
|
|
4
|
+
import EvDbStreamAddress from "@eventualize/types/EvDbStreamAddress";
|
|
5
|
+
import EvDbEvent from "@eventualize/types/EvDbEvent";
|
|
6
|
+
import EvDbViewAddress from "@eventualize/types/EvDbViewAddress";
|
|
7
|
+
import { EvDbStoredSnapshotData } from "@eventualize/types/EvDbStoredSnapshotData";
|
|
8
|
+
export declare class EventRecord {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly stream_cursor: EvDbStreamCursor;
|
|
11
|
+
readonly event_type: string;
|
|
12
|
+
readonly captured_by: string;
|
|
13
|
+
readonly captured_at: Date;
|
|
14
|
+
readonly payload: IEvDbEventPayload;
|
|
15
|
+
readonly stored_at?: Date | undefined;
|
|
16
|
+
constructor(id: string, stream_cursor: EvDbStreamCursor, event_type: string, captured_by: string, captured_at: Date, payload: IEvDbEventPayload, stored_at?: Date | undefined);
|
|
17
|
+
static createFromEvent(e: EvDbEvent): EventRecord;
|
|
18
|
+
toEvDbEvent(): EvDbEvent;
|
|
19
|
+
}
|
|
20
|
+
export type MessageRecord = {
|
|
21
|
+
id: string;
|
|
22
|
+
stream_cursor: EvDbStreamCursor;
|
|
23
|
+
channel: string;
|
|
24
|
+
message_type: string;
|
|
25
|
+
event_type: string;
|
|
26
|
+
captured_by: string;
|
|
27
|
+
captured_at: Date;
|
|
28
|
+
payload: IEvDbPayloadData;
|
|
29
|
+
stored_at?: Date;
|
|
30
|
+
};
|
|
31
|
+
export declare const deserializeStreamAddress: (streamAddressStr: string) => EvDbStreamAddress;
|
|
32
|
+
export default class EvDbDynamoDbStorageAdapterQueries {
|
|
33
|
+
static saveEvents(events: EventRecord[]): TransactWriteItem[];
|
|
34
|
+
static saveMessages(messages: MessageRecord[]): TransactWriteItem[];
|
|
35
|
+
static getLastOffset(streamAddress: EvDbStreamAddress): QueryCommand;
|
|
36
|
+
static getEvents(streamCursor: EvDbStreamCursor, queryCursor?: Record<string, any> | undefined, pageSize?: number): QueryCommand;
|
|
37
|
+
static getSnapshot(viewAddress: EvDbViewAddress): QueryCommand;
|
|
38
|
+
static saveSnapshot(snapshot: EvDbStoredSnapshotData): PutItemCommand;
|
|
39
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { PutItemCommand, QueryCommand } from "@aws-sdk/client-dynamodb";
|
|
2
|
+
import EvDbStreamAddress from "@eventualize/types/EvDbStreamAddress";
|
|
3
|
+
import EvDbEvent from "@eventualize/types/EvDbEvent";
|
|
4
|
+
import EvDbViewAddress from "@eventualize/types/EvDbViewAddress";
|
|
5
|
+
import { marshall } from "@aws-sdk/util-dynamodb";
|
|
6
|
+
export class EventRecord {
|
|
7
|
+
constructor(id, stream_cursor, event_type, captured_by, captured_at, payload, stored_at) {
|
|
8
|
+
this.id = id;
|
|
9
|
+
this.stream_cursor = stream_cursor;
|
|
10
|
+
this.event_type = event_type;
|
|
11
|
+
this.captured_by = captured_by;
|
|
12
|
+
this.captured_at = captured_at;
|
|
13
|
+
this.payload = payload;
|
|
14
|
+
this.stored_at = stored_at;
|
|
15
|
+
}
|
|
16
|
+
static createFromEvent(e) {
|
|
17
|
+
return new EventRecord(crypto.randomUUID(), e.streamCursor, e.eventType, e.capturedBy, e.capturedAt, e.payload, e.storedAt);
|
|
18
|
+
}
|
|
19
|
+
toEvDbEvent() {
|
|
20
|
+
return new EvDbEvent(this.event_type, this.stream_cursor, this.payload, this.captured_at, this.captured_by, new Date(Number(this.stored_at)));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const serializeStreamAddress = (streamAddress) => {
|
|
24
|
+
return `${streamAddress.streamType}::${streamAddress.streamId}`;
|
|
25
|
+
};
|
|
26
|
+
export const deserializeStreamAddress = (streamAddressStr) => {
|
|
27
|
+
const [streamType, streamId] = streamAddressStr.split('::');
|
|
28
|
+
return new EvDbStreamAddress(streamType, streamId);
|
|
29
|
+
};
|
|
30
|
+
const serializeMessageAddress = (m) => {
|
|
31
|
+
return `${m.channel}::${m.message_type}`;
|
|
32
|
+
};
|
|
33
|
+
const serializeViewAddress = (viewAddress) => {
|
|
34
|
+
return `${serializeStreamAddress(viewAddress)}::${viewAddress.viewName}`;
|
|
35
|
+
};
|
|
36
|
+
export default class EvDbDynamoDbStorageAdapterQueries {
|
|
37
|
+
static saveEvents(events) {
|
|
38
|
+
const TransactItems = events.map(e => ({
|
|
39
|
+
Put: {
|
|
40
|
+
TableName: "events",
|
|
41
|
+
Item: {
|
|
42
|
+
stream_address: { S: serializeStreamAddress(e.stream_cursor) },
|
|
43
|
+
offset: { N: e.stream_cursor.offset.toString() },
|
|
44
|
+
event_type: { S: e.event_type },
|
|
45
|
+
captured_by: { S: e.captured_by },
|
|
46
|
+
captured_at: { S: e.captured_at.getTime().toString() },
|
|
47
|
+
payload: {
|
|
48
|
+
M: marshall(e.payload, {
|
|
49
|
+
convertClassInstanceToMap: true,
|
|
50
|
+
removeUndefinedValues: true
|
|
51
|
+
})
|
|
52
|
+
},
|
|
53
|
+
stored_at: { S: Date.now().toString() }
|
|
54
|
+
},
|
|
55
|
+
ConditionExpression: "(attribute_not_exists(#sa)) Or (attribute_exists(#sa) And attribute_not_exists(#offset))",
|
|
56
|
+
ExpressionAttributeNames: {
|
|
57
|
+
"#sa": "stream_address",
|
|
58
|
+
"#offset": "offset"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}));
|
|
62
|
+
return TransactItems;
|
|
63
|
+
}
|
|
64
|
+
static saveMessages(messages) {
|
|
65
|
+
const TransactItems = messages.map(m => ({
|
|
66
|
+
Put: {
|
|
67
|
+
TableName: "messages",
|
|
68
|
+
Item: {
|
|
69
|
+
message_address: { S: serializeMessageAddress(m) },
|
|
70
|
+
stream_address: { S: serializeStreamAddress(m.stream_cursor) },
|
|
71
|
+
offset: { N: m.stream_cursor.offset.toString() },
|
|
72
|
+
event_type: { S: m.event_type },
|
|
73
|
+
captured_by: { S: m.captured_by },
|
|
74
|
+
captured_at: { S: `${m.captured_at.getTime().toString()}::${crypto.randomUUID()}` },
|
|
75
|
+
payload: {
|
|
76
|
+
M: marshall(m.payload, {
|
|
77
|
+
convertClassInstanceToMap: true,
|
|
78
|
+
removeUndefinedValues: true
|
|
79
|
+
})
|
|
80
|
+
},
|
|
81
|
+
stored_at: { S: Date.now().toString() }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}));
|
|
85
|
+
return TransactItems;
|
|
86
|
+
}
|
|
87
|
+
static getLastOffset(streamAddress) {
|
|
88
|
+
const queryParams = {
|
|
89
|
+
TableName: "events",
|
|
90
|
+
KeyConditionExpression: "stream_address = :pk",
|
|
91
|
+
ExpressionAttributeValues: {
|
|
92
|
+
":pk": { S: serializeStreamAddress(streamAddress) }
|
|
93
|
+
},
|
|
94
|
+
ScanIndexForward: false,
|
|
95
|
+
Limit: 1
|
|
96
|
+
};
|
|
97
|
+
return new QueryCommand(queryParams);
|
|
98
|
+
}
|
|
99
|
+
static getEvents(streamCursor, queryCursor = undefined, pageSize = 100) {
|
|
100
|
+
const queryParams = {
|
|
101
|
+
TableName: "events",
|
|
102
|
+
KeyConditionExpression: "#sa = :sa AND #o >= :offsetValue",
|
|
103
|
+
ExpressionAttributeNames: {
|
|
104
|
+
"#o": "offset",
|
|
105
|
+
"#sa": "stream_address",
|
|
106
|
+
"#cb": "captured_by"
|
|
107
|
+
},
|
|
108
|
+
ExpressionAttributeValues: {
|
|
109
|
+
":sa": { S: serializeStreamAddress(streamCursor) },
|
|
110
|
+
":offsetValue": { N: streamCursor.offset.toString() }
|
|
111
|
+
},
|
|
112
|
+
ProjectionExpression: '#sa, #o, id, event_type, captured_at, #cb, stored_at, payload',
|
|
113
|
+
Limit: pageSize,
|
|
114
|
+
ExclusiveStartKey: queryCursor
|
|
115
|
+
};
|
|
116
|
+
return new QueryCommand(queryParams);
|
|
117
|
+
}
|
|
118
|
+
static getSnapshot(viewAddress) {
|
|
119
|
+
const queryParams = {
|
|
120
|
+
TableName: "snapshots",
|
|
121
|
+
KeyConditionExpression: "view_address = :sa",
|
|
122
|
+
ExpressionAttributeValues: {
|
|
123
|
+
":sa": { S: serializeViewAddress(viewAddress) },
|
|
124
|
+
},
|
|
125
|
+
ProjectionExpression: '#o, #s, stored_at',
|
|
126
|
+
ExpressionAttributeNames: {
|
|
127
|
+
"#o": "offset",
|
|
128
|
+
"#s": "state"
|
|
129
|
+
},
|
|
130
|
+
ScanIndexForward: false, // false = descending order
|
|
131
|
+
Limit: 1
|
|
132
|
+
};
|
|
133
|
+
return new QueryCommand(queryParams);
|
|
134
|
+
}
|
|
135
|
+
static saveSnapshot(snapshot) {
|
|
136
|
+
const viewAddress = new EvDbViewAddress(snapshot.streamType, snapshot.streamId, snapshot.viewName);
|
|
137
|
+
const queryParams = {
|
|
138
|
+
TableName: "snapshots",
|
|
139
|
+
Item: {
|
|
140
|
+
view_address: { S: serializeViewAddress(viewAddress) },
|
|
141
|
+
offset: { N: snapshot.offset.toString() },
|
|
142
|
+
state: {
|
|
143
|
+
M: marshall(snapshot.state, {
|
|
144
|
+
convertClassInstanceToMap: true,
|
|
145
|
+
removeUndefinedValues: true
|
|
146
|
+
})
|
|
147
|
+
},
|
|
148
|
+
stored_at: { S: Date.now().toString() }
|
|
149
|
+
},
|
|
150
|
+
ConditionExpression: "(attribute_not_exists(#va)) Or (attribute_exists(#va) And attribute_not_exists(#offset))",
|
|
151
|
+
ExpressionAttributeNames: {
|
|
152
|
+
"#va": "view_address",
|
|
153
|
+
"#offset": "offset"
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
return new PutItemCommand(queryParams);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=EvDbDynamoDbStorageAdapterQueries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EvDbDynamoDbStorageAdapterQueries.js","sourceRoot":"","sources":["../src/EvDbDynamoDbStorageAdapterQueries.ts"],"names":[],"mappings":"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"}
|