Skip to content

Commit a5c8aaf

Browse files
Introduce EndMessage (#216)
* Update protocol.proto * Introduce EndMessage * Update tests
1 parent 1785bd4 commit a5c8aaf

14 files changed

+212
-54
lines changed

proto/protocol.proto

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,6 @@ message CompletionMessage {
5252
};
5353
}
5454

55-
// Type: 0x0000 + 4
56-
message EntryAckMessage {
57-
uint32 entry_index = 1;
58-
}
59-
6055
// Type: 0x0000 + 2
6156
// Implementations MUST send this message when suspending an invocation.
6257
message SuspensionMessage {
@@ -83,6 +78,16 @@ message ErrorMessage {
8378
string description = 3;
8479
}
8580

81+
// Type: 0x0000 + 4
82+
message EntryAckMessage {
83+
uint32 entry_index = 1;
84+
}
85+
86+
// Type: 0x0000 + 5
87+
// Implementations MUST send this message when the invocation lifecycle ends.
88+
message EndMessage {
89+
}
90+
8691
// --- Journal Entries ---
8792

8893
// Every Completable JournalEntry has a result field, filled only and only if the entry is in DONE state.

src/state_machine.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { rlog } from "./utils/logger";
1919
import { clearTimeout } from "timers";
2020
import {
2121
COMPLETION_MESSAGE_TYPE,
22+
END_MESSAGE_TYPE,
23+
EndMessage,
2224
ENTRY_ACK_MESSAGE_TYPE,
2325
ERROR_MESSAGE_TYPE,
2426
OUTPUT_STREAM_ENTRY_MESSAGE_TYPE,
@@ -282,6 +284,9 @@ export class StateMachine<I, O> implements RestateStreamConsumer {
282284
"Function completed successfully."
283285
);
284286

287+
// Mark the end of the invocation
288+
this.send(new Message(END_MESSAGE_TYPE, EndMessage.create()));
289+
285290
this.finish(value);
286291
} catch (e) {
287292
this.unhandledError(ensureError(e));
@@ -303,7 +308,7 @@ export class StateMachine<I, O> implements RestateStreamConsumer {
303308
"Function completed with an error: " + error.message
304309
);
305310

306-
this.finishWithError(error);
311+
this.sendErrorAndFinish(error);
307312
} catch (ee) {
308313
this.unhandledError(ensureError(ee));
309314
}
@@ -314,7 +319,7 @@ export class StateMachine<I, O> implements RestateStreamConsumer {
314319
return this.invocationComplete.promise;
315320
}
316321

317-
private async finishWithError(e: Error) {
322+
private async sendErrorAndFinish(e: Error) {
318323
if (e instanceof TerminalError) {
319324
this.sendTerminalError(e);
320325
} else {
@@ -354,6 +359,9 @@ export class StateMachine<I, O> implements RestateStreamConsumer {
354359
if (!this.journal.outputMsgWasReplayed()) {
355360
this.send(msg);
356361
}
362+
363+
// Mark the end of the invocation
364+
this.send(new Message(END_MESSAGE_TYPE, EndMessage.create()));
357365
}
358366

359367
private send(message: Message) {
@@ -464,7 +472,7 @@ export class StateMachine<I, O> implements RestateStreamConsumer {
464472
}
465473

466474
public async notifyHandlerExecutionError(e: RetryableError | TerminalError) {
467-
await this.finishWithError(e);
475+
await this.sendErrorAndFinish(e);
468476
}
469477

470478
/**

src/types/protocol.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
CompletionMessage,
1919
EntryAckMessage,
2020
ErrorMessage,
21+
EndMessage,
2122
GetStateEntryMessage,
2223
InvokeEntryMessage,
2324
OutputStreamEntryMessage,
@@ -36,6 +37,7 @@ export {
3637
CompleteAwakeableEntryMessage,
3738
CompletionMessage,
3839
ErrorMessage,
40+
EndMessage,
3941
GetStateEntryMessage,
4042
InvokeEntryMessage,
4143
OutputStreamEntryMessage,
@@ -53,6 +55,7 @@ export const COMPLETION_MESSAGE_TYPE = 0x0001n;
5355
export const SUSPENSION_MESSAGE_TYPE = 0x0002n;
5456
export const ERROR_MESSAGE_TYPE = 0x0003n;
5557
export const ENTRY_ACK_MESSAGE_TYPE = 0x0004n;
58+
export const END_MESSAGE_TYPE = 0x0005n;
5659
export const POLL_INPUT_STREAM_ENTRY_MESSAGE_TYPE = 0x0400n;
5760
export const OUTPUT_STREAM_ENTRY_MESSAGE_TYPE = 0x0401n;
5861
export const GET_STATE_ENTRY_MESSAGE_TYPE = 0x0800n;
@@ -78,6 +81,7 @@ export const KNOWN_MESSAGE_TYPES = new Set([
7881
SUSPENSION_MESSAGE_TYPE,
7982
ERROR_MESSAGE_TYPE,
8083
ENTRY_ACK_MESSAGE_TYPE,
84+
END_MESSAGE_TYPE,
8185
POLL_INPUT_STREAM_ENTRY_MESSAGE_TYPE,
8286
OUTPUT_STREAM_ENTRY_MESSAGE_TYPE,
8387
GET_STATE_ENTRY_MESSAGE_TYPE,
@@ -97,6 +101,7 @@ export const PROTOBUF_MESSAGE_NAME_BY_TYPE = new Map<bigint, string>([
97101
[SUSPENSION_MESSAGE_TYPE, "SuspensionMessage"],
98102
[ERROR_MESSAGE_TYPE, "ErrorMessage"],
99103
[ENTRY_ACK_MESSAGE_TYPE, "EntryAckMessage"],
104+
[END_MESSAGE_TYPE, "EndMessage"],
100105
[POLL_INPUT_STREAM_ENTRY_MESSAGE_TYPE, "PollInputStreamEntryMessage"],
101106
[OUTPUT_STREAM_ENTRY_MESSAGE_TYPE, "OutputStreamEntryMessage"],
102107
[GET_STATE_ENTRY_MESSAGE_TYPE, "GetStateEntryMessage"],
@@ -117,6 +122,7 @@ const PROTOBUF_MESSAGES: Array<[bigint, any]> = [
117122
[SUSPENSION_MESSAGE_TYPE, SuspensionMessage],
118123
[ERROR_MESSAGE_TYPE, ErrorMessage],
119124
[ENTRY_ACK_MESSAGE_TYPE, EntryAckMessage],
125+
[END_MESSAGE_TYPE, EndMessage],
120126
[POLL_INPUT_STREAM_ENTRY_MESSAGE_TYPE, PollInputStreamEntryMessage],
121127
[OUTPUT_STREAM_ENTRY_MESSAGE_TYPE, OutputStreamEntryMessage],
122128
[GET_STATE_ENTRY_MESSAGE_TYPE, GetStateEntryMessage],
@@ -138,6 +144,7 @@ export type ProtocolMessage =
138144
| SuspensionMessage
139145
| ErrorMessage
140146
| EntryAckMessage
147+
| EndMessage
141148
| PollInputStreamEntryMessage
142149
| OutputStreamEntryMessage
143150
| GetStateEntryMessage

test/awakeable.test.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
outputMessage,
2727
startMessage,
2828
suspensionMessage,
29+
END_MESSAGE,
2930
} from "./protoutils";
3031
import { TestGreeter, TestResponse } from "../src/generated/proto/test";
3132
import { ProtocolMode } from "../src/generated/proto/discovery";
@@ -74,6 +75,7 @@ describe("AwakeableGreeter", () => {
7475
expect(result).toStrictEqual([
7576
awakeableMessage(),
7677
outputMessage(greetResponse(`Hello Francesco for ${getAwakeableId(1)}`)),
78+
END_MESSAGE,
7779
]);
7880
});
7981

@@ -87,6 +89,7 @@ describe("AwakeableGreeter", () => {
8789
expect(result).toStrictEqual([
8890
awakeableMessage(),
8991
outputMessage(greetResponse(`Hello for ${getAwakeableId(1)}`)),
92+
END_MESSAGE,
9093
]);
9194
});
9295

@@ -102,6 +105,7 @@ describe("AwakeableGreeter", () => {
102105
outputMessage(
103106
greetResponse(`Hello [object Object] for ${getAwakeableId(1)}`)
104107
),
108+
END_MESSAGE,
105109
]);
106110
});
107111

@@ -117,9 +121,10 @@ describe("AwakeableGreeter", () => {
117121
),
118122
]).run();
119123

120-
expect(result.length).toStrictEqual(2);
124+
expect(result.length).toStrictEqual(3);
121125
expect(result[0]).toStrictEqual(awakeableMessage());
122126
checkTerminalError(result[1], "Something went wrong");
127+
expect(result[2]).toStrictEqual(END_MESSAGE);
123128
});
124129

125130
it("handles replay with value", async () => {
@@ -131,6 +136,7 @@ describe("AwakeableGreeter", () => {
131136

132137
expect(result).toStrictEqual([
133138
outputMessage(greetResponse(`Hello Francesco for ${getAwakeableId(1)}`)),
139+
END_MESSAGE,
134140
]);
135141
});
136142

@@ -141,8 +147,9 @@ describe("AwakeableGreeter", () => {
141147
awakeableMessage(undefined, failure("Something went wrong")),
142148
]).run();
143149

144-
expect(result.length).toStrictEqual(1);
150+
expect(result.length).toStrictEqual(2);
145151
checkTerminalError(result[0], "Something went wrong");
152+
expect(result[1]).toStrictEqual(END_MESSAGE);
146153
});
147154

148155
it("fails on journal mismatch. Completed with CompleteAwakeable during replay.", async () => {

test/complete_awakeable.test.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
outputMessage,
2323
startMessage,
2424
rejectAwakeableMessage,
25+
END_MESSAGE,
2526
} from "./protoutils";
2627
import { describe, expect } from "@jest/globals";
2728
import { TestDriver } from "./testdriver";
@@ -49,6 +50,7 @@ describe("ResolveAwakeableGreeter", () => {
4950
expect(result).toStrictEqual([
5051
resolveAwakeableMessage(getAwakeableId(1), "hello"),
5152
outputMessage(greetResponse("Hello")),
53+
END_MESSAGE,
5254
]);
5355
});
5456

@@ -61,6 +63,7 @@ describe("ResolveAwakeableGreeter", () => {
6163
expect(result).toStrictEqual([
6264
resolveAwakeableMessage(getAwakeableId(1), ""),
6365
outputMessage(greetResponse("Hello")),
66+
END_MESSAGE,
6467
]);
6568
});
6669

@@ -71,7 +74,10 @@ describe("ResolveAwakeableGreeter", () => {
7174
resolveAwakeableMessage(getAwakeableId(1), "hello"),
7275
]).run();
7376

74-
expect(result).toStrictEqual([outputMessage(greetResponse("Hello"))]);
77+
expect(result).toStrictEqual([
78+
outputMessage(greetResponse("Hello")),
79+
END_MESSAGE,
80+
]);
7581
});
7682

7783
it("handles replay with value empty string", async () => {
@@ -81,7 +87,10 @@ describe("ResolveAwakeableGreeter", () => {
8187
resolveAwakeableMessage(getAwakeableId(1), ""),
8288
]).run();
8389

84-
expect(result).toStrictEqual([outputMessage(greetResponse("Hello"))]);
90+
expect(result).toStrictEqual([
91+
outputMessage(greetResponse("Hello")),
92+
END_MESSAGE,
93+
]);
8594
});
8695

8796
it("fails on journal mismatch. Completed with invoke during replay.", async () => {
@@ -138,6 +147,7 @@ describe("RejectAwakeableGreeter", () => {
138147
expect(result).toStrictEqual([
139148
rejectAwakeableMessage(getAwakeableId(1), "my bad error"),
140149
outputMessage(greetResponse("Hello")),
150+
END_MESSAGE,
141151
]);
142152
});
143153
});

test/eager_state.test.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { TestDriver } from "./testdriver";
1919
import {
2020
clearStateMessage,
2121
completionMessage,
22+
END_MESSAGE,
2223
getStateMessage,
2324
greetRequest,
2425
greetResponse,
@@ -55,6 +56,7 @@ describe("GetEmpty", () => {
5556
expect(result).toStrictEqual([
5657
getStateMessage("STATE", undefined, true),
5758
outputMessage(greetResponse("true")),
59+
END_MESSAGE,
5860
]);
5961
});
6062

@@ -78,7 +80,10 @@ describe("GetEmpty", () => {
7880
ProtocolMode.BIDI_STREAM
7981
).run();
8082

81-
expect(result).toStrictEqual([outputMessage(greetResponse("true"))]);
83+
expect(result).toStrictEqual([
84+
outputMessage(greetResponse("true")),
85+
END_MESSAGE,
86+
]);
8287
});
8388
});
8489

@@ -103,6 +108,7 @@ describe("Get", () => {
103108
expect(result).toStrictEqual([
104109
getStateMessage("STATE", "One"),
105110
outputMessage(greetResponse("One")),
111+
END_MESSAGE,
106112
]);
107113
});
108114

@@ -116,6 +122,7 @@ describe("Get", () => {
116122
expect(result).toStrictEqual([
117123
getStateMessage("STATE", "One"),
118124
outputMessage(greetResponse("One")),
125+
END_MESSAGE,
119126
]);
120127
});
121128

@@ -158,6 +165,7 @@ describe("GetAppendAndGet", () => {
158165
setStateMessage("STATE", "OneTwo"),
159166
getStateMessage("STATE", "OneTwo"),
160167
outputMessage(greetResponse("OneTwo")),
168+
END_MESSAGE,
161169
]);
162170
});
163171

@@ -173,6 +181,7 @@ describe("GetAppendAndGet", () => {
173181
setStateMessage("STATE", "OneTwo"),
174182
getStateMessage("STATE", "OneTwo"),
175183
outputMessage(greetResponse("OneTwo")),
184+
END_MESSAGE,
176185
]);
177186
});
178187
});
@@ -202,6 +211,7 @@ describe("GetClearAndGet", () => {
202211
clearStateMessage("STATE"),
203212
getStateMessage("STATE", undefined, true),
204213
outputMessage(greetResponse("One-nothing")),
214+
END_MESSAGE,
205215
]);
206216
});
207217

@@ -217,6 +227,7 @@ describe("GetClearAndGet", () => {
217227
clearStateMessage("STATE"),
218228
getStateMessage("STATE", undefined, true),
219229
outputMessage(greetResponse("One-nothing")),
230+
END_MESSAGE,
220231
]);
221232
});
222233
});
@@ -249,6 +260,7 @@ describe("MultipleGet", () => {
249260
getStateMessage("STATE", "One"),
250261
getStateMessage("STATE", "One"),
251262
outputMessage(greetResponse("One - One - One")),
263+
END_MESSAGE,
252264
]);
253265
});
254266

@@ -264,6 +276,7 @@ describe("MultipleGet", () => {
264276
getStateMessage("STATE", "One"),
265277
getStateMessage("STATE", "One"),
266278
outputMessage(greetResponse("One - One - One")),
279+
END_MESSAGE,
267280
]);
268281
});
269282
});

0 commit comments

Comments
 (0)