Skip to content

Commit 01c3491

Browse files
authored
Refactor multi stream append to use v2 protocol (#439)
* Refactor multi stream append to use v2 protocol
1 parent e438513 commit 01c3491

35 files changed

+4237
-5666
lines changed

docs/api/appending-events.md

Lines changed: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -205,23 +205,12 @@ This feature is only available in KurrentDB 25.1 and later.
205205

206206
You can append events to multiple streams in a single atomic operation. Either all streams are updated, or the entire operation fails.
207207

208-
The `multiStreamAppend` method accepts a collection of `AppendStreamRequest` objects and returns a `MultiAppendResult`. Each `AppendStreamRequest` contains:
209-
210-
- **streamName** - The name of the stream
211-
- **expectedState** - The expected state of the stream for optimistic concurrency control
212-
- **events** - A collection of `EventData` objects to append
213-
214-
The operation returns either:
215-
- `AppendStreamSuccess` - Successful append results for all streams
216-
- `AppendStreamFailure` - Specific exceptions for any failed operations
217-
218208
::: warning
219-
Event metadata in `EventData` must be valid JSON objects. This requirement will
220-
be removed in a future major release.
209+
Currently, metadata must be valid JSON. Binary metadata will not be supported in
210+
this version. This limitation ensures compatibility with KurrentDB's metadata
211+
handling and will be removed in the next major release.
221212
:::
222213

223-
Here's a basic example of appending events to multiple streams:
224-
225214
```ts
226215
import { jsonEvent } from "@kurrent/kurrentdb-client";
227216
import { v4 as uuid } from "uuid";
@@ -265,43 +254,5 @@ const requests = [
265254
}
266255
];
267256

268-
const result = await client.multiStreamAppend(requests);
269-
270-
if (result.success) {
271-
result.output.forEach((success) => {
272-
console.log(`Stream '${success.streamName}' updated at position ${success.position}`);
273-
});
274-
}
275-
```
276-
277-
If the operation doesn't succeed, it can fail with the following exceptions:
278-
279-
```ts
280-
const result = await client.multiStreamAppend(requests);
281-
282-
if (!result.success) {
283-
result.output.forEach((failure) => {
284-
switch (failure.details.type) {
285-
case "wrong_expected_revision":
286-
console.log(`Version conflict in stream '${failure.streamName}': expected revision ${failure.details.revision}`);
287-
break;
288-
289-
case "access_denied":
290-
console.log(`Access denied to stream '${failure.streamName}': ${failure.details.reason}`);
291-
break;
292-
293-
case "stream_deleted":
294-
console.log(`Stream '${failure.streamName}' was deleted`);
295-
break;
296-
297-
case "transaction_max_size_exceeded":
298-
console.log(`Transaction too large for stream '${failure.streamName}': max size is ${failure.details.maxSize} bytes`);
299-
break;
300-
301-
default:
302-
console.log(`Unexpected error for stream '${failure.streamName}': ${failure.details.type}`);
303-
break;
304-
}
305-
});
306-
}
307-
```
257+
await client.multiStreamAppend(requests);
258+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// GENERATED CODE -- NO SERVICES IN PROTO
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// package: kurrent.rpc
2+
// file: kurrentdb/protocols/v2/errors.proto
3+
4+
/* tslint:disable */
5+
/* eslint-disable */
6+
7+
import * as jspb from "google-protobuf";
8+
import * as kurrentdb_protocols_v2_rpc_pb from "../../../kurrentdb/protocols/v2/rpc_pb";
9+
10+
export class AccessDeniedErrorDetails extends jspb.Message {
11+
getOperation(): string;
12+
setOperation(value: string): AccessDeniedErrorDetails;
13+
14+
hasUsername(): boolean;
15+
clearUsername(): void;
16+
getUsername(): string | undefined;
17+
setUsername(value: string): AccessDeniedErrorDetails;
18+
19+
hasPermission(): boolean;
20+
clearPermission(): void;
21+
getPermission(): string | undefined;
22+
setPermission(value: string): AccessDeniedErrorDetails;
23+
24+
serializeBinary(): Uint8Array;
25+
toObject(includeInstance?: boolean): AccessDeniedErrorDetails.AsObject;
26+
static toObject(includeInstance: boolean, msg: AccessDeniedErrorDetails): AccessDeniedErrorDetails.AsObject;
27+
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
28+
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
29+
static serializeBinaryToWriter(message: AccessDeniedErrorDetails, writer: jspb.BinaryWriter): void;
30+
static deserializeBinary(bytes: Uint8Array): AccessDeniedErrorDetails;
31+
static deserializeBinaryFromReader(message: AccessDeniedErrorDetails, reader: jspb.BinaryReader): AccessDeniedErrorDetails;
32+
}
33+
34+
export namespace AccessDeniedErrorDetails {
35+
export type AsObject = {
36+
operation: string,
37+
username?: string,
38+
permission?: string,
39+
}
40+
}
41+
42+
export class NotLeaderNodeErrorDetails extends jspb.Message {
43+
44+
hasCurrentLeader(): boolean;
45+
clearCurrentLeader(): void;
46+
getCurrentLeader(): NotLeaderNodeErrorDetails.NodeInfo | undefined;
47+
setCurrentLeader(value?: NotLeaderNodeErrorDetails.NodeInfo): NotLeaderNodeErrorDetails;
48+
49+
serializeBinary(): Uint8Array;
50+
toObject(includeInstance?: boolean): NotLeaderNodeErrorDetails.AsObject;
51+
static toObject(includeInstance: boolean, msg: NotLeaderNodeErrorDetails): NotLeaderNodeErrorDetails.AsObject;
52+
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
53+
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
54+
static serializeBinaryToWriter(message: NotLeaderNodeErrorDetails, writer: jspb.BinaryWriter): void;
55+
static deserializeBinary(bytes: Uint8Array): NotLeaderNodeErrorDetails;
56+
static deserializeBinaryFromReader(message: NotLeaderNodeErrorDetails, reader: jspb.BinaryReader): NotLeaderNodeErrorDetails;
57+
}
58+
59+
export namespace NotLeaderNodeErrorDetails {
60+
export type AsObject = {
61+
currentLeader?: NotLeaderNodeErrorDetails.NodeInfo.AsObject,
62+
}
63+
64+
65+
export class NodeInfo extends jspb.Message {
66+
getHost(): string;
67+
setHost(value: string): NodeInfo;
68+
getPort(): number;
69+
setPort(value: number): NodeInfo;
70+
71+
hasNodeId(): boolean;
72+
clearNodeId(): void;
73+
getNodeId(): string | undefined;
74+
setNodeId(value: string): NodeInfo;
75+
76+
serializeBinary(): Uint8Array;
77+
toObject(includeInstance?: boolean): NodeInfo.AsObject;
78+
static toObject(includeInstance: boolean, msg: NodeInfo): NodeInfo.AsObject;
79+
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
80+
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
81+
static serializeBinaryToWriter(message: NodeInfo, writer: jspb.BinaryWriter): void;
82+
static deserializeBinary(bytes: Uint8Array): NodeInfo;
83+
static deserializeBinaryFromReader(message: NodeInfo, reader: jspb.BinaryReader): NodeInfo;
84+
}
85+
86+
export namespace NodeInfo {
87+
export type AsObject = {
88+
host: string,
89+
port: number,
90+
nodeId?: string,
91+
}
92+
}
93+
94+
}
95+
96+
export enum ServerError {
97+
UNSPECIFIED = 0,
98+
SERVER_ERROR_ACCESS_DENIED = 1,
99+
SERVER_ERROR_BAD_REQUEST = 2,
100+
SERVER_ERROR_NOT_LEADER_NODE = 5,
101+
SERVER_ERROR_OPERATION_TIMEOUT = 6,
102+
SERVER_ERROR_SERVER_NOT_READY = 7,
103+
SERVER_ERROR_SERVER_OVERLOADED = 8,
104+
SERVER_ERROR_SERVER_MALFUNCTION = 9,
105+
}

0 commit comments

Comments
 (0)