@chronon-sim/engine 0.1.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/LICENSE +202 -0
- package/README.md +19 -0
- package/dist/components/autoscaler.d.ts +47 -0
- package/dist/components/autoscaler.d.ts.map +1 -0
- package/dist/components/autoscaler.js +116 -0
- package/dist/components/autoscaler.js.map +1 -0
- package/dist/components/generator.d.ts +32 -0
- package/dist/components/generator.d.ts.map +1 -0
- package/dist/components/generator.js +124 -0
- package/dist/components/generator.js.map +1 -0
- package/dist/components/index.d.ts +14 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +11 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/message-queue.d.ts +45 -0
- package/dist/components/message-queue.d.ts.map +1 -0
- package/dist/components/message-queue.js +152 -0
- package/dist/components/message-queue.js.map +1 -0
- package/dist/components/pullable.d.ts +24 -0
- package/dist/components/pullable.d.ts.map +1 -0
- package/dist/components/pullable.js +2 -0
- package/dist/components/pullable.js.map +1 -0
- package/dist/components/queue.d.ts +32 -0
- package/dist/components/queue.d.ts.map +1 -0
- package/dist/components/queue.js +88 -0
- package/dist/components/queue.js.map +1 -0
- package/dist/components/rate-limiter.d.ts +24 -0
- package/dist/components/rate-limiter.d.ts.map +1 -0
- package/dist/components/rate-limiter.js +77 -0
- package/dist/components/rate-limiter.js.map +1 -0
- package/dist/components/registry.d.ts +19 -0
- package/dist/components/registry.d.ts.map +1 -0
- package/dist/components/registry.js +43 -0
- package/dist/components/registry.js.map +1 -0
- package/dist/components/retry-policy.d.ts +41 -0
- package/dist/components/retry-policy.d.ts.map +1 -0
- package/dist/components/retry-policy.js +122 -0
- package/dist/components/retry-policy.js.map +1 -0
- package/dist/components/server.d.ts +26 -0
- package/dist/components/server.d.ts.map +1 -0
- package/dist/components/server.js +82 -0
- package/dist/components/server.js.map +1 -0
- package/dist/components/sink.d.ts +12 -0
- package/dist/components/sink.d.ts.map +1 -0
- package/dist/components/sink.js +35 -0
- package/dist/components/sink.js.map +1 -0
- package/dist/components/worker-pool.d.ts +68 -0
- package/dist/components/worker-pool.d.ts.map +1 -0
- package/dist/components/worker-pool.js +224 -0
- package/dist/components/worker-pool.js.map +1 -0
- package/dist/format/definition.d.ts +72 -0
- package/dist/format/definition.d.ts.map +1 -0
- package/dist/format/definition.js +17 -0
- package/dist/format/definition.js.map +1 -0
- package/dist/format/index.d.ts +5 -0
- package/dist/format/index.d.ts.map +1 -0
- package/dist/format/index.js +3 -0
- package/dist/format/index.js.map +1 -0
- package/dist/format/loader.d.ts +19 -0
- package/dist/format/loader.d.ts.map +1 -0
- package/dist/format/loader.js +275 -0
- package/dist/format/loader.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel/heap.d.ts +14 -0
- package/dist/kernel/heap.d.ts.map +1 -0
- package/dist/kernel/heap.js +55 -0
- package/dist/kernel/heap.js.map +1 -0
- package/dist/kernel/index.d.ts +3 -0
- package/dist/kernel/index.d.ts.map +1 -0
- package/dist/kernel/index.js +2 -0
- package/dist/kernel/index.js.map +1 -0
- package/dist/kernel/kernel.d.ts +104 -0
- package/dist/kernel/kernel.d.ts.map +1 -0
- package/dist/kernel/kernel.js +236 -0
- package/dist/kernel/kernel.js.map +1 -0
- package/dist/model/entity.d.ts +78 -0
- package/dist/model/entity.d.ts.map +1 -0
- package/dist/model/entity.js +108 -0
- package/dist/model/entity.js.map +1 -0
- package/dist/model/fifo.d.ts +9 -0
- package/dist/model/fifo.d.ts.map +1 -0
- package/dist/model/fifo.js +24 -0
- package/dist/model/fifo.js.map +1 -0
- package/dist/model/index.d.ts +4 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/index.js +3 -0
- package/dist/model/index.js.map +1 -0
- package/dist/rng/distributions.d.ts +98 -0
- package/dist/rng/distributions.d.ts.map +1 -0
- package/dist/rng/distributions.js +224 -0
- package/dist/rng/distributions.js.map +1 -0
- package/dist/rng/index.d.ts +4 -0
- package/dist/rng/index.d.ts.map +1 -0
- package/dist/rng/index.js +3 -0
- package/dist/rng/index.js.map +1 -0
- package/dist/rng/rng.d.ts +26 -0
- package/dist/rng/rng.d.ts.map +1 -0
- package/dist/rng/rng.js +101 -0
- package/dist/rng/rng.js.map +1 -0
- package/dist/run/assertions.d.ts +9 -0
- package/dist/run/assertions.d.ts.map +1 -0
- package/dist/run/assertions.js +57 -0
- package/dist/run/assertions.js.map +1 -0
- package/dist/run/index.d.ts +6 -0
- package/dist/run/index.d.ts.map +1 -0
- package/dist/run/index.js +4 -0
- package/dist/run/index.js.map +1 -0
- package/dist/run/results.d.ts +72 -0
- package/dist/run/results.d.ts.map +1 -0
- package/dist/run/results.js +2 -0
- package/dist/run/results.js.map +1 -0
- package/dist/run/run.d.ts +29 -0
- package/dist/run/run.d.ts.map +1 -0
- package/dist/run/run.js +80 -0
- package/dist/run/run.js.map +1 -0
- package/dist/run/simulation.d.ts +34 -0
- package/dist/run/simulation.d.ts.map +1 -0
- package/dist/run/simulation.js +104 -0
- package/dist/run/simulation.js.map +1 -0
- package/dist/schema/index.d.ts +5 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +3 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/types.d.ts +83 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +19 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/schema/validate.d.ts +19 -0
- package/dist/schema/validate.d.ts.map +1 -0
- package/dist/schema/validate.js +120 -0
- package/dist/schema/validate.js.map +1 -0
- package/dist/stats/index.d.ts +6 -0
- package/dist/stats/index.d.ts.map +1 -0
- package/dist/stats/index.js +4 -0
- package/dist/stats/index.js.map +1 -0
- package/dist/stats/percentile.d.ts +38 -0
- package/dist/stats/percentile.d.ts.map +1 -0
- package/dist/stats/percentile.js +62 -0
- package/dist/stats/percentile.js.map +1 -0
- package/dist/stats/summary.d.ts +20 -0
- package/dist/stats/summary.d.ts.map +1 -0
- package/dist/stats/summary.js +38 -0
- package/dist/stats/summary.js.map +1 -0
- package/dist/stats/time-weighted.d.ts +31 -0
- package/dist/stats/time-weighted.d.ts.map +1 -0
- package/dist/stats/time-weighted.js +69 -0
- package/dist/stats/time-weighted.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { LinkedComponent, type ComponentInit, type MovingEntity, type SimContext } from "../model/index.js";
|
|
2
|
+
import type { ComponentSchema } from "../schema/index.js";
|
|
3
|
+
import type { Lease, PullSource, QueueWaiter } from "./pullable.js";
|
|
4
|
+
/**
|
|
5
|
+
* SQS-style message queue with a visibility timeout and an optional dead-letter queue.
|
|
6
|
+
*
|
|
7
|
+
* A consumer `receive`s a message, which hides it for `visibilityTimeout` seconds. If the consumer
|
|
8
|
+
* `ack`s in time the message is deleted. Otherwise it becomes visible again (a redelivery). When a
|
|
9
|
+
* message has been received `maxReceiveCount` times and its visibility timeout expires once more,
|
|
10
|
+
* it moves to the `deadLetter` link (or is discarded and counted if there is none).
|
|
11
|
+
*
|
|
12
|
+
* A consumer whose work outlives the visibility timeout is *not* stopped: it finishes, its ack is
|
|
13
|
+
* stale (returns false), and the message may be processed again by someone else. That duplicate
|
|
14
|
+
* processing is the classic visibility-timeout failure mode, and it is modelled on purpose.
|
|
15
|
+
*/
|
|
16
|
+
export declare class MessageQueue extends LinkedComponent implements PullSource {
|
|
17
|
+
static readonly schema: ComponentSchema<MessageQueue>;
|
|
18
|
+
readonly redeliversUnacked = true;
|
|
19
|
+
private readonly visible;
|
|
20
|
+
private readonly waiters;
|
|
21
|
+
private readonly visibilityTicks;
|
|
22
|
+
private readonly maxReceiveCount;
|
|
23
|
+
private readonly lengthStat;
|
|
24
|
+
private readonly flightStat;
|
|
25
|
+
private readonly waitTally;
|
|
26
|
+
private deadLetter;
|
|
27
|
+
private flight;
|
|
28
|
+
private received;
|
|
29
|
+
private redelivered;
|
|
30
|
+
private deadLettered;
|
|
31
|
+
private requests;
|
|
32
|
+
private readonly pricePerRequest;
|
|
33
|
+
constructor(ctx: SimContext, init: ComponentInit);
|
|
34
|
+
private get now();
|
|
35
|
+
setLink(key: string, target: LinkedComponent): void;
|
|
36
|
+
addWaiter(waiter: QueueWaiter): void;
|
|
37
|
+
addEntity(entity: MovingEntity): void;
|
|
38
|
+
receive(): Lease | null;
|
|
39
|
+
private expire;
|
|
40
|
+
private makeVisible;
|
|
41
|
+
private visibleChanged;
|
|
42
|
+
private setFlight;
|
|
43
|
+
resetStatistics(): void;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=message-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-queue.d.ts","sourceRoot":"","sources":["../../src/components/message-queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAExH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAUpE;;;;;;;;;;;GAWG;AACH,qBAAa,YAAa,SAAQ,eAAgB,YAAW,UAAU;IACrE,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,YAAY,CAAC,CAyBnD;IAEF,QAAQ,CAAC,iBAAiB,QAAQ;IAElC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAE7B,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa;IAUhD,OAAO,KAAK,GAAG,GAEd;IAEQ,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAK5D,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAI3B,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAM9C,OAAO,IAAI,KAAK,GAAG,IAAI;IA6BvB,OAAO,CAAC,MAAM;IAad,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,SAAS;IAKR,eAAe,IAAI,IAAI;CAUjC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { Priority } from "../kernel/index.js";
|
|
2
|
+
import { FifoBuffer, LinkedComponent } from "../model/index.js";
|
|
3
|
+
import { Tally, TimeWeightedStat } from "../stats/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* SQS-style message queue with a visibility timeout and an optional dead-letter queue.
|
|
6
|
+
*
|
|
7
|
+
* A consumer `receive`s a message, which hides it for `visibilityTimeout` seconds. If the consumer
|
|
8
|
+
* `ack`s in time the message is deleted. Otherwise it becomes visible again (a redelivery). When a
|
|
9
|
+
* message has been received `maxReceiveCount` times and its visibility timeout expires once more,
|
|
10
|
+
* it moves to the `deadLetter` link (or is discarded and counted if there is none).
|
|
11
|
+
*
|
|
12
|
+
* A consumer whose work outlives the visibility timeout is *not* stopped: it finishes, its ack is
|
|
13
|
+
* stale (returns false), and the message may be processed again by someone else. That duplicate
|
|
14
|
+
* processing is the classic visibility-timeout failure mode, and it is modelled on purpose.
|
|
15
|
+
*/
|
|
16
|
+
export class MessageQueue extends LinkedComponent {
|
|
17
|
+
static schema = {
|
|
18
|
+
type: "MessageQueue",
|
|
19
|
+
description: "SQS-style queue: received messages are hidden for a visibility timeout and redelivered unless acknowledged; after maxReceiveCount receives they go to a dead-letter queue.",
|
|
20
|
+
roles: ["receiver", "pullable"],
|
|
21
|
+
inputs: [
|
|
22
|
+
{ key: "visibilityTimeout", type: "number", unit: "time", min: 0.000001, default: 30, required: false, description: "Seconds a received message stays hidden before it is redelivered." },
|
|
23
|
+
{ key: "maxReceiveCount", type: "integer", unit: "dimensionless", min: 1, required: false, description: "Receives allowed before a message is dead-lettered (when its visibility timeout expires again). Unlimited if omitted." },
|
|
24
|
+
{ key: "costPerMillionRequests", type: "number", unit: "cost", min: 0, default: 0, required: false, description: "Price per million API requests (each send, receive and delete counts as one)." },
|
|
25
|
+
],
|
|
26
|
+
links: [{ key: "deadLetter", description: "Where messages that exhausted maxReceiveCount are sent. They are discarded (and counted) if omitted.", required: false, accepts: "receiver" }],
|
|
27
|
+
outputs: [
|
|
28
|
+
{ key: "QueueLength", unit: "dimensionless", series: true, description: "Messages visible and waiting to be received.", get: (q) => q.visible.length },
|
|
29
|
+
{ key: "InFlight", unit: "dimensionless", series: true, description: "Messages received but not yet acknowledged or expired.", get: (q) => q.flight },
|
|
30
|
+
{ key: "Backlog", unit: "dimensionless", series: true, description: "Visible plus in-flight messages (SQS ApproximateNumberOfMessages + NotVisible).", get: (q) => q.visible.length + q.flight },
|
|
31
|
+
{ key: "AverageQueueLength", unit: "dimensionless", description: "Time-weighted average number of visible messages.", get: (q) => q.lengthStat.mean(q.now) },
|
|
32
|
+
{ key: "MaxQueueLength", unit: "dimensionless", description: "Largest number of visible messages.", get: (q) => q.lengthStat.max(q.now) },
|
|
33
|
+
{ key: "AverageInFlight", unit: "dimensionless", description: "Time-weighted average number of in-flight messages.", get: (q) => q.flightStat.mean(q.now) },
|
|
34
|
+
{ key: "AverageQueueTime", unit: "time", description: "Mean time a message was visible before being received (each receive counts).", get: (q) => q.waitTally.mean() },
|
|
35
|
+
{ key: "NumberRequests", unit: "dimensionless", description: "Billable API requests: sends + receives + deletes.", get: (q) => q.requests },
|
|
36
|
+
{ key: "Cost", unit: "cost", description: "Request charges over the measured window.", get: (q) => q.requests * q.pricePerRequest },
|
|
37
|
+
{ key: "NumberReceived", unit: "dimensionless", description: "Receive operations (a redelivered message counts each time).", get: (q) => q.received },
|
|
38
|
+
{ key: "NumberRedelivered", unit: "dimensionless", description: "Messages that became visible again because the visibility timeout expired.", get: (q) => q.redelivered },
|
|
39
|
+
{ key: "NumberDeadLettered", unit: "dimensionless", description: "Messages that exhausted maxReceiveCount.", get: (q) => q.deadLettered },
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
redeliversUnacked = true;
|
|
43
|
+
visible = new FifoBuffer();
|
|
44
|
+
waiters = [];
|
|
45
|
+
visibilityTicks;
|
|
46
|
+
maxReceiveCount;
|
|
47
|
+
lengthStat;
|
|
48
|
+
flightStat;
|
|
49
|
+
waitTally = new Tally();
|
|
50
|
+
deadLetter = null;
|
|
51
|
+
flight = 0;
|
|
52
|
+
received = 0;
|
|
53
|
+
redelivered = 0;
|
|
54
|
+
deadLettered = 0;
|
|
55
|
+
requests = 0;
|
|
56
|
+
pricePerRequest;
|
|
57
|
+
constructor(ctx, init) {
|
|
58
|
+
super(ctx, init, "Empty");
|
|
59
|
+
// At least one tick, so a tiny timeout can never expire within the tick of its receive.
|
|
60
|
+
this.visibilityTicks = Math.max(1, ctx.kernel.secondsToTicks(init.inputs["visibilityTimeout"]));
|
|
61
|
+
this.maxReceiveCount = init.inputs["maxReceiveCount"];
|
|
62
|
+
this.pricePerRequest = init.inputs["costPerMillionRequests"] / 1_000_000;
|
|
63
|
+
this.lengthStat = new TimeWeightedStat(ctx.kernel.currentTick, 0);
|
|
64
|
+
this.flightStat = new TimeWeightedStat(ctx.kernel.currentTick, 0);
|
|
65
|
+
}
|
|
66
|
+
get now() {
|
|
67
|
+
return this.ctx.kernel.currentTick;
|
|
68
|
+
}
|
|
69
|
+
setLink(key, target) {
|
|
70
|
+
if (key === "deadLetter")
|
|
71
|
+
this.deadLetter = target;
|
|
72
|
+
else
|
|
73
|
+
super.setLink(key, target);
|
|
74
|
+
}
|
|
75
|
+
addWaiter(waiter) {
|
|
76
|
+
this.waiters.push(waiter);
|
|
77
|
+
}
|
|
78
|
+
addEntity(entity) {
|
|
79
|
+
this.noteAdded();
|
|
80
|
+
this.requests++;
|
|
81
|
+
this.makeVisible({ entity, receiveCount: 0, visibleAt: this.now });
|
|
82
|
+
}
|
|
83
|
+
receive() {
|
|
84
|
+
const msg = this.visible.shift();
|
|
85
|
+
if (msg === undefined)
|
|
86
|
+
return null;
|
|
87
|
+
msg.receiveCount++;
|
|
88
|
+
this.received++;
|
|
89
|
+
this.requests++;
|
|
90
|
+
this.waitTally.add(this.ctx.kernel.ticksToSeconds(this.now - msg.visibleAt));
|
|
91
|
+
this.setFlight(this.flight + 1);
|
|
92
|
+
this.visibleChanged();
|
|
93
|
+
let active = true;
|
|
94
|
+
const timer = this.ctx.kernel.schedule(this.visibilityTicks, Priority.DEFAULT, () => {
|
|
95
|
+
active = false;
|
|
96
|
+
this.expire(msg);
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
entity: msg.entity,
|
|
100
|
+
ack: () => {
|
|
101
|
+
if (!active)
|
|
102
|
+
return false;
|
|
103
|
+
active = false;
|
|
104
|
+
timer.cancel();
|
|
105
|
+
this.requests++;
|
|
106
|
+
this.setFlight(this.flight - 1);
|
|
107
|
+
this.noteCompleted();
|
|
108
|
+
return true;
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
expire(msg) {
|
|
113
|
+
this.setFlight(this.flight - 1);
|
|
114
|
+
if (this.maxReceiveCount !== undefined && msg.receiveCount >= this.maxReceiveCount) {
|
|
115
|
+
this.deadLettered++;
|
|
116
|
+
this.noteCompleted();
|
|
117
|
+
this.deadLetter?.addEntity(msg.entity);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
this.redelivered++;
|
|
121
|
+
msg.visibleAt = this.now;
|
|
122
|
+
this.makeVisible(msg);
|
|
123
|
+
}
|
|
124
|
+
makeVisible(msg) {
|
|
125
|
+
this.visible.push(msg);
|
|
126
|
+
this.visibleChanged();
|
|
127
|
+
for (const w of this.waiters) {
|
|
128
|
+
if (this.visible.length === 0)
|
|
129
|
+
break;
|
|
130
|
+
w.queueHasEntity();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
visibleChanged() {
|
|
134
|
+
this.lengthStat.set(this.now, this.visible.length);
|
|
135
|
+
this.setState(this.visible.length === 0 ? "Empty" : "NonEmpty");
|
|
136
|
+
}
|
|
137
|
+
setFlight(n) {
|
|
138
|
+
this.flight = n;
|
|
139
|
+
this.flightStat.set(this.now, n);
|
|
140
|
+
}
|
|
141
|
+
resetStatistics() {
|
|
142
|
+
super.resetStatistics();
|
|
143
|
+
this.lengthStat.reset(this.now);
|
|
144
|
+
this.flightStat.reset(this.now);
|
|
145
|
+
this.waitTally.reset();
|
|
146
|
+
this.received = 0;
|
|
147
|
+
this.redelivered = 0;
|
|
148
|
+
this.deadLettered = 0;
|
|
149
|
+
this.requests = 0;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=message-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-queue.js","sourceRoot":"","sources":["../../src/components/message-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { MovingEntity } from "../model/index.js";
|
|
2
|
+
/** Something that wants to be told when a queue has entities available. */
|
|
3
|
+
export interface QueueWaiter {
|
|
4
|
+
queueHasEntity(): void;
|
|
5
|
+
}
|
|
6
|
+
/** One received entity. `ack` confirms it was processed. */
|
|
7
|
+
export interface Lease {
|
|
8
|
+
readonly entity: MovingEntity;
|
|
9
|
+
/**
|
|
10
|
+
* Confirm successful processing. Returns false if the lease is stale, i.e. the source already
|
|
11
|
+
* took the entity back (an SQS visibility timeout expired) and the confirmation has no effect.
|
|
12
|
+
*/
|
|
13
|
+
ack(): boolean;
|
|
14
|
+
}
|
|
15
|
+
/** A source that workers pull entities from: a plain Queue or a MessageQueue. */
|
|
16
|
+
export interface PullSource {
|
|
17
|
+
/** True if an entity that is received but never acked comes back (visibility-timeout semantics). */
|
|
18
|
+
readonly redeliversUnacked: boolean;
|
|
19
|
+
/** Take the next available entity, or null if there is none. */
|
|
20
|
+
receive(): Lease | null;
|
|
21
|
+
/** Register a waiter to be notified whenever an entity becomes available. */
|
|
22
|
+
addWaiter(waiter: QueueWaiter): void;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=pullable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pullable.d.ts","sourceRoot":"","sources":["../../src/components/pullable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,cAAc,IAAI,IAAI,CAAC;CACxB;AAED,4DAA4D;AAC5D,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B;;;OAGG;IACH,GAAG,IAAI,OAAO,CAAC;CAChB;AAED,iFAAiF;AACjF,MAAM,WAAW,UAAU;IACzB,oGAAoG;IACpG,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,gEAAgE;IAChE,OAAO,IAAI,KAAK,GAAG,IAAI,CAAC;IACxB,6EAA6E;IAC7E,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;CACtC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pullable.js","sourceRoot":"","sources":["../../src/components/pullable.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { LinkedComponent, type ComponentInit, type MovingEntity, type SimContext } from "../model/index.js";
|
|
2
|
+
import type { ComponentSchema } from "../schema/index.js";
|
|
3
|
+
import type { Lease, PullSource, QueueWaiter } from "./pullable.js";
|
|
4
|
+
/**
|
|
5
|
+
* FIFO queue with an optional maximum length. Arrivals beyond `maxLength` are dropped and counted.
|
|
6
|
+
* Servers pull entities with {@link Queue.poll}; waiting servers are notified on arrival.
|
|
7
|
+
* WorkerPools pull through the {@link PullSource} interface.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Queue extends LinkedComponent implements PullSource {
|
|
10
|
+
static readonly schema: ComponentSchema<Queue>;
|
|
11
|
+
private readonly items;
|
|
12
|
+
private readonly waiters;
|
|
13
|
+
private readonly maxLength;
|
|
14
|
+
private readonly lengthStat;
|
|
15
|
+
private readonly waitTally;
|
|
16
|
+
private dropped;
|
|
17
|
+
constructor(ctx: SimContext, init: ComponentInit);
|
|
18
|
+
private get now();
|
|
19
|
+
/** Entities currently waiting. */
|
|
20
|
+
get length(): number;
|
|
21
|
+
/** Register a server to be notified whenever an entity is enqueued. */
|
|
22
|
+
addWaiter(waiter: QueueWaiter): void;
|
|
23
|
+
addEntity(entity: MovingEntity): void;
|
|
24
|
+
/** Remove and return the oldest waiting entity, or null if empty. */
|
|
25
|
+
poll(): MovingEntity | null;
|
|
26
|
+
/** A plain queue never takes entities back: a received entity is gone, and `ack` always succeeds. */
|
|
27
|
+
readonly redeliversUnacked = false;
|
|
28
|
+
receive(): Lease | null;
|
|
29
|
+
private lengthChanged;
|
|
30
|
+
resetStatistics(): void;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/components/queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAExH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEpE;;;;GAIG;AACH,qBAAa,KAAM,SAAQ,eAAgB,YAAW,UAAU;IAC9D,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,KAAK,CAAC,CAe5C;IAEF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiE;IACvF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,OAAO,CAAK;gBAER,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa;IAMhD,OAAO,KAAK,GAAG,GAEd;IAED,kCAAkC;IAClC,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,uEAAuE;IACvE,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAI3B,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAc9C,qEAAqE;IACrE,IAAI,IAAI,YAAY,GAAG,IAAI;IAS3B,qGAAqG;IACrG,QAAQ,CAAC,iBAAiB,SAAS;IAEnC,OAAO,IAAI,KAAK,GAAG,IAAI;IAKvB,OAAO,CAAC,aAAa;IAKZ,eAAe,IAAI,IAAI;CAMjC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { FifoBuffer, LinkedComponent } from "../model/index.js";
|
|
2
|
+
import { Tally, TimeWeightedStat } from "../stats/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* FIFO queue with an optional maximum length. Arrivals beyond `maxLength` are dropped and counted.
|
|
5
|
+
* Servers pull entities with {@link Queue.poll}; waiting servers are notified on arrival.
|
|
6
|
+
* WorkerPools pull through the {@link PullSource} interface.
|
|
7
|
+
*/
|
|
8
|
+
export class Queue extends LinkedComponent {
|
|
9
|
+
static schema = {
|
|
10
|
+
type: "Queue",
|
|
11
|
+
description: "FIFO queue with an optional capacity. Arrivals beyond maxLength are dropped and counted.",
|
|
12
|
+
roles: ["receiver", "queue", "pullable"],
|
|
13
|
+
inputs: [
|
|
14
|
+
{ key: "maxLength", type: "integer", unit: "dimensionless", min: 1, required: false, description: "Maximum number of waiting entities. Unlimited if omitted." },
|
|
15
|
+
],
|
|
16
|
+
links: [],
|
|
17
|
+
outputs: [
|
|
18
|
+
{ key: "QueueLength", unit: "dimensionless", series: true, description: "Entities currently waiting.", get: (q) => q.length },
|
|
19
|
+
{ key: "AverageQueueLength", unit: "dimensionless", description: "Time-weighted average number waiting.", get: (q) => q.lengthStat.mean(q.now) },
|
|
20
|
+
{ key: "MaxQueueLength", unit: "dimensionless", description: "Largest number waiting.", get: (q) => q.lengthStat.max(q.now) },
|
|
21
|
+
{ key: "AverageQueueTime", unit: "time", description: "Mean time an entity waited before leaving the queue.", get: (q) => q.waitTally.mean() },
|
|
22
|
+
{ key: "NumberDropped", unit: "dimensionless", description: "Arrivals rejected because the queue was full.", get: (q) => q.dropped },
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
items = new FifoBuffer();
|
|
26
|
+
waiters = [];
|
|
27
|
+
maxLength;
|
|
28
|
+
lengthStat;
|
|
29
|
+
waitTally = new Tally();
|
|
30
|
+
dropped = 0;
|
|
31
|
+
constructor(ctx, init) {
|
|
32
|
+
super(ctx, init, "Empty");
|
|
33
|
+
this.maxLength = init.inputs["maxLength"];
|
|
34
|
+
this.lengthStat = new TimeWeightedStat(ctx.kernel.currentTick, 0);
|
|
35
|
+
}
|
|
36
|
+
get now() {
|
|
37
|
+
return this.ctx.kernel.currentTick;
|
|
38
|
+
}
|
|
39
|
+
/** Entities currently waiting. */
|
|
40
|
+
get length() {
|
|
41
|
+
return this.items.length;
|
|
42
|
+
}
|
|
43
|
+
/** Register a server to be notified whenever an entity is enqueued. */
|
|
44
|
+
addWaiter(waiter) {
|
|
45
|
+
this.waiters.push(waiter);
|
|
46
|
+
}
|
|
47
|
+
addEntity(entity) {
|
|
48
|
+
if (this.maxLength !== undefined && this.items.length >= this.maxLength) {
|
|
49
|
+
this.dropped++;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.noteAdded();
|
|
53
|
+
this.items.push({ entity, enteredAt: this.now });
|
|
54
|
+
this.lengthChanged();
|
|
55
|
+
for (const w of this.waiters) {
|
|
56
|
+
if (this.items.length === 0)
|
|
57
|
+
break;
|
|
58
|
+
w.queueHasEntity();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** Remove and return the oldest waiting entity, or null if empty. */
|
|
62
|
+
poll() {
|
|
63
|
+
const item = this.items.shift();
|
|
64
|
+
if (item === undefined)
|
|
65
|
+
return null;
|
|
66
|
+
this.waitTally.add(this.ctx.kernel.ticksToSeconds(this.now - item.enteredAt));
|
|
67
|
+
this.lengthChanged();
|
|
68
|
+
this.noteCompleted();
|
|
69
|
+
return item.entity;
|
|
70
|
+
}
|
|
71
|
+
/** A plain queue never takes entities back: a received entity is gone, and `ack` always succeeds. */
|
|
72
|
+
redeliversUnacked = false;
|
|
73
|
+
receive() {
|
|
74
|
+
const entity = this.poll();
|
|
75
|
+
return entity === null ? null : { entity, ack: () => true };
|
|
76
|
+
}
|
|
77
|
+
lengthChanged() {
|
|
78
|
+
this.lengthStat.set(this.now, this.items.length);
|
|
79
|
+
this.setState(this.items.length === 0 ? "Empty" : "NonEmpty");
|
|
80
|
+
}
|
|
81
|
+
resetStatistics() {
|
|
82
|
+
super.resetStatistics();
|
|
83
|
+
this.lengthStat.reset(this.now);
|
|
84
|
+
this.waitTally.reset();
|
|
85
|
+
this.dropped = 0;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/components/queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { LinkedComponent, type ComponentInit, type MovingEntity, type SimContext } from "../model/index.js";
|
|
2
|
+
import type { ComponentSchema } from "../schema/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Token-bucket rate limiter (an API-gateway-style throttle). The bucket starts full with `burst`
|
|
5
|
+
* tokens and refills continuously at `rate` tokens per second, never exceeding `burst`. Each entity
|
|
6
|
+
* takes one token and is passed to `next`; with no token it is rejected: sent to `onReject`, or
|
|
7
|
+
* dropped and counted. Deterministic: it uses no randomness.
|
|
8
|
+
*/
|
|
9
|
+
export declare class RateLimiter extends LinkedComponent {
|
|
10
|
+
static readonly schema: ComponentSchema<RateLimiter>;
|
|
11
|
+
private readonly rate;
|
|
12
|
+
private readonly burst;
|
|
13
|
+
private tokens;
|
|
14
|
+
private lastTick;
|
|
15
|
+
private onReject;
|
|
16
|
+
private allowed;
|
|
17
|
+
private rejected;
|
|
18
|
+
constructor(ctx: SimContext, init: ComponentInit);
|
|
19
|
+
setLink(key: string, target: LinkedComponent): void;
|
|
20
|
+
private currentTokens;
|
|
21
|
+
addEntity(entity: MovingEntity): void;
|
|
22
|
+
resetStatistics(): void;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/components/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC5G,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAK1D;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,eAAe;IAC9C,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,WAAW,CAAC,CAkBlD;IAEF,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAgC;IAChD,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAAK;gBAET,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa;IAQvC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAK5D,OAAO,CAAC,aAAa;IAUZ,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAYrC,eAAe,IAAI,IAAI;CAKjC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { LinkedComponent } from "../model/index.js";
|
|
2
|
+
/** Float slack so that e.g. five refills of 0.2 tokens count as one whole token. */
|
|
3
|
+
const EPSILON = 1e-9;
|
|
4
|
+
/**
|
|
5
|
+
* Token-bucket rate limiter (an API-gateway-style throttle). The bucket starts full with `burst`
|
|
6
|
+
* tokens and refills continuously at `rate` tokens per second, never exceeding `burst`. Each entity
|
|
7
|
+
* takes one token and is passed to `next`; with no token it is rejected: sent to `onReject`, or
|
|
8
|
+
* dropped and counted. Deterministic: it uses no randomness.
|
|
9
|
+
*/
|
|
10
|
+
export class RateLimiter extends LinkedComponent {
|
|
11
|
+
static schema = {
|
|
12
|
+
type: "RateLimiter",
|
|
13
|
+
description: "Token-bucket rate limiter: passes entities while tokens last, rejects the rest.",
|
|
14
|
+
roles: ["receiver"],
|
|
15
|
+
inputs: [
|
|
16
|
+
{ key: "rate", type: "number", unit: "rate", min: 0, required: true, description: "Tokens added per second (the sustained allowed rate)." },
|
|
17
|
+
{ key: "burst", type: "number", unit: "dimensionless", min: 1, required: true, description: "Bucket size: the largest burst that passes at once. The bucket starts full." },
|
|
18
|
+
],
|
|
19
|
+
links: [
|
|
20
|
+
{ key: "next", description: "Where allowed entities are sent.", required: false, accepts: "receiver" },
|
|
21
|
+
{ key: "onReject", description: "Where rejected entities are sent. Dropped and counted if omitted.", required: false, accepts: "receiver" },
|
|
22
|
+
],
|
|
23
|
+
outputs: [
|
|
24
|
+
{ key: "NumberAllowed", unit: "dimensionless", description: "Entities that got a token.", get: (l) => l.allowed },
|
|
25
|
+
{ key: "NumberRejected", unit: "dimensionless", description: "Entities rejected for lack of a token.", get: (l) => l.rejected },
|
|
26
|
+
{ key: "RejectionFraction", unit: "dimensionless", description: "Rejected / (allowed + rejected).", get: (l) => (l.allowed + l.rejected === 0 ? NaN : l.rejected / (l.allowed + l.rejected)) },
|
|
27
|
+
{ key: "Tokens", unit: "dimensionless", series: true, description: "Tokens currently in the bucket.", get: (l) => l.currentTokens() },
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
rate;
|
|
31
|
+
burst;
|
|
32
|
+
tokens;
|
|
33
|
+
lastTick;
|
|
34
|
+
onReject = null;
|
|
35
|
+
allowed = 0;
|
|
36
|
+
rejected = 0;
|
|
37
|
+
constructor(ctx, init) {
|
|
38
|
+
super(ctx, init, "Idle");
|
|
39
|
+
this.rate = init.inputs["rate"];
|
|
40
|
+
this.burst = init.inputs["burst"];
|
|
41
|
+
this.tokens = this.burst;
|
|
42
|
+
this.lastTick = ctx.kernel.currentTick;
|
|
43
|
+
}
|
|
44
|
+
setLink(key, target) {
|
|
45
|
+
if (key === "onReject")
|
|
46
|
+
this.onReject = target;
|
|
47
|
+
else
|
|
48
|
+
super.setLink(key, target);
|
|
49
|
+
}
|
|
50
|
+
currentTokens() {
|
|
51
|
+
const now = this.ctx.kernel.currentTick;
|
|
52
|
+
if (now > this.lastTick) {
|
|
53
|
+
const seconds = this.ctx.kernel.ticksToSeconds(now - this.lastTick);
|
|
54
|
+
this.tokens = Math.min(this.burst, this.tokens + seconds * this.rate);
|
|
55
|
+
this.lastTick = now;
|
|
56
|
+
}
|
|
57
|
+
return this.tokens;
|
|
58
|
+
}
|
|
59
|
+
addEntity(entity) {
|
|
60
|
+
if (this.currentTokens() >= 1 - EPSILON) {
|
|
61
|
+
this.tokens = Math.max(0, this.tokens - 1);
|
|
62
|
+
this.allowed++;
|
|
63
|
+
this.noteAdded();
|
|
64
|
+
this.sendToNext(entity);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.rejected++;
|
|
68
|
+
this.onReject?.addEntity(entity);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
resetStatistics() {
|
|
72
|
+
super.resetStatistics();
|
|
73
|
+
this.allowed = 0;
|
|
74
|
+
this.rejected = 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/components/rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ComponentInit, LinkedComponent, SimContext } from "../model/index.js";
|
|
2
|
+
import type { ComponentSchema } from "../schema/index.js";
|
|
3
|
+
/** A component class: a static schema plus a constructor. */
|
|
4
|
+
export interface ComponentClass {
|
|
5
|
+
readonly schema: ComponentSchema;
|
|
6
|
+
new (ctx: SimContext, init: ComponentInit): LinkedComponent;
|
|
7
|
+
}
|
|
8
|
+
/** Maps component type names (as used in model files) to classes. */
|
|
9
|
+
export declare class ComponentRegistry {
|
|
10
|
+
private readonly classes;
|
|
11
|
+
register(cls: ComponentClass): this;
|
|
12
|
+
get(type: string): ComponentClass | undefined;
|
|
13
|
+
/** Registered type names, in registration order. */
|
|
14
|
+
types(): string[];
|
|
15
|
+
schemas(): ComponentSchema[];
|
|
16
|
+
}
|
|
17
|
+
/** A registry containing every built-in component: the generic ones and the cloud ones. */
|
|
18
|
+
export declare function createDefaultRegistry(): ComponentRegistry;
|
|
19
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/components/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAW1D,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,KAAK,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,GAAG,eAAe,CAAC;CAC7D;AAED,qEAAqE;AACrE,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAE7D,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI;IAMnC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI7C,oDAAoD;IACpD,KAAK,IAAI,MAAM,EAAE;IAIjB,OAAO,IAAI,eAAe,EAAE;CAG7B;AAED,2FAA2F;AAC3F,wBAAgB,qBAAqB,IAAI,iBAAiB,CAWzD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EntityGenerator } from "./generator.js";
|
|
2
|
+
import { Autoscaler } from "./autoscaler.js";
|
|
3
|
+
import { MessageQueue } from "./message-queue.js";
|
|
4
|
+
import { Queue } from "./queue.js";
|
|
5
|
+
import { RateLimiter } from "./rate-limiter.js";
|
|
6
|
+
import { RetryPolicy } from "./retry-policy.js";
|
|
7
|
+
import { Server } from "./server.js";
|
|
8
|
+
import { EntitySink } from "./sink.js";
|
|
9
|
+
import { WorkerPool } from "./worker-pool.js";
|
|
10
|
+
/** Maps component type names (as used in model files) to classes. */
|
|
11
|
+
export class ComponentRegistry {
|
|
12
|
+
classes = new Map();
|
|
13
|
+
register(cls) {
|
|
14
|
+
if (this.classes.has(cls.schema.type))
|
|
15
|
+
throw new Error(`component type already registered: ${cls.schema.type}`);
|
|
16
|
+
this.classes.set(cls.schema.type, cls);
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
get(type) {
|
|
20
|
+
return this.classes.get(type);
|
|
21
|
+
}
|
|
22
|
+
/** Registered type names, in registration order. */
|
|
23
|
+
types() {
|
|
24
|
+
return [...this.classes.keys()];
|
|
25
|
+
}
|
|
26
|
+
schemas() {
|
|
27
|
+
return [...this.classes.values()].map((c) => c.schema);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** A registry containing every built-in component: the generic ones and the cloud ones. */
|
|
31
|
+
export function createDefaultRegistry() {
|
|
32
|
+
return new ComponentRegistry()
|
|
33
|
+
.register(EntityGenerator)
|
|
34
|
+
.register(Queue)
|
|
35
|
+
.register(Server)
|
|
36
|
+
.register(EntitySink)
|
|
37
|
+
.register(MessageQueue)
|
|
38
|
+
.register(WorkerPool)
|
|
39
|
+
.register(RetryPolicy)
|
|
40
|
+
.register(RateLimiter)
|
|
41
|
+
.register(Autoscaler);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/components/registry.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQ9C,qEAAqE;AACrE,MAAM,OAAO,iBAAiB;IACX,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAE7D,QAAQ,CAAC,GAAmB;QAC1B,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAChH,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,oDAAoD;IACpD,KAAK;QACH,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,OAAO;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;CACF;AAED,2FAA2F;AAC3F,MAAM,UAAU,qBAAqB;IACnC,OAAO,IAAI,iBAAiB,EAAE;SAC3B,QAAQ,CAAC,eAAe,CAAC;SACzB,QAAQ,CAAC,KAAK,CAAC;SACf,QAAQ,CAAC,MAAM,CAAC;SAChB,QAAQ,CAAC,UAAU,CAAC;SACpB,QAAQ,CAAC,YAAY,CAAC;SACtB,QAAQ,CAAC,UAAU,CAAC;SACpB,QAAQ,CAAC,WAAW,CAAC;SACrB,QAAQ,CAAC,WAAW,CAAC;SACrB,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { LinkedComponent, type ComponentInit, type MovingEntity, type SimContext } from "../model/index.js";
|
|
2
|
+
import type { ComponentSchema } from "../schema/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Retries failed attempts with exponential backoff and jitter.
|
|
5
|
+
*
|
|
6
|
+
* Wiring: send entities *into* the RetryPolicy, point `next` at the thing that can fail (a
|
|
7
|
+
* WorkerPool), and point that thing's `onFailure` / `onThrottle` links back at the RetryPolicy. The
|
|
8
|
+
* first time an entity arrives it is a fresh request and is forwarded immediately. If it comes back
|
|
9
|
+
* it is a failed attempt: after a backoff delay it is forwarded again, until `maxAttempts` attempts
|
|
10
|
+
* have been made, at which point it goes to `giveUp` (or is dropped and counted).
|
|
11
|
+
*
|
|
12
|
+
* The attempt count lives on the entity, so an entity should pass through a given RetryPolicy once.
|
|
13
|
+
*
|
|
14
|
+
* Delay before retry n (n = attempts made so far, starting at 1):
|
|
15
|
+
* `d = min(maxDelay, baseDelay * multiplier^(n-1))`, then jitter: `none` uses d, `full` draws
|
|
16
|
+
* uniformly from [0, d], `equal` draws from [d/2, d] (the AWS Architecture Blog definitions).
|
|
17
|
+
*/
|
|
18
|
+
export declare class RetryPolicy extends LinkedComponent {
|
|
19
|
+
static readonly schema: ComponentSchema<RetryPolicy>;
|
|
20
|
+
private readonly attemptKey;
|
|
21
|
+
private readonly maxAttempts;
|
|
22
|
+
private readonly baseDelay;
|
|
23
|
+
private readonly multiplier;
|
|
24
|
+
private readonly maxDelay;
|
|
25
|
+
private readonly jitter;
|
|
26
|
+
private readonly unit;
|
|
27
|
+
private giveUpTarget;
|
|
28
|
+
private requests;
|
|
29
|
+
private attempts;
|
|
30
|
+
private retries;
|
|
31
|
+
private givenUp;
|
|
32
|
+
private waiting;
|
|
33
|
+
constructor(ctx: SimContext, init: ComponentInit);
|
|
34
|
+
setLink(key: string, target: LinkedComponent): void;
|
|
35
|
+
addEntity(entity: MovingEntity): void;
|
|
36
|
+
private send;
|
|
37
|
+
/** Delay in seconds before the retry that follows `made` attempts. */
|
|
38
|
+
private backoff;
|
|
39
|
+
resetStatistics(): void;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=retry-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-policy.d.ts","sourceRoot":"","sources":["../../src/components/retry-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE5G,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D;;;;;;;;;;;;;;GAcG;AACH,qBAAa,WAAY,SAAQ,eAAe;IAC9C,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,WAAW,CAAC,CAuBlD;IAEF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiB;IACtC,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAK;gBAER,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa;IAYvC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAKnD,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAyB9C,OAAO,CAAC,IAAI;IAKZ,sEAAsE;IACtE,OAAO,CAAC,OAAO;IAYN,eAAe,IAAI,IAAI;CAOjC"}
|