@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.
Files changed (151) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +19 -0
  3. package/dist/components/autoscaler.d.ts +47 -0
  4. package/dist/components/autoscaler.d.ts.map +1 -0
  5. package/dist/components/autoscaler.js +116 -0
  6. package/dist/components/autoscaler.js.map +1 -0
  7. package/dist/components/generator.d.ts +32 -0
  8. package/dist/components/generator.d.ts.map +1 -0
  9. package/dist/components/generator.js +124 -0
  10. package/dist/components/generator.js.map +1 -0
  11. package/dist/components/index.d.ts +14 -0
  12. package/dist/components/index.d.ts.map +1 -0
  13. package/dist/components/index.js +11 -0
  14. package/dist/components/index.js.map +1 -0
  15. package/dist/components/message-queue.d.ts +45 -0
  16. package/dist/components/message-queue.d.ts.map +1 -0
  17. package/dist/components/message-queue.js +152 -0
  18. package/dist/components/message-queue.js.map +1 -0
  19. package/dist/components/pullable.d.ts +24 -0
  20. package/dist/components/pullable.d.ts.map +1 -0
  21. package/dist/components/pullable.js +2 -0
  22. package/dist/components/pullable.js.map +1 -0
  23. package/dist/components/queue.d.ts +32 -0
  24. package/dist/components/queue.d.ts.map +1 -0
  25. package/dist/components/queue.js +88 -0
  26. package/dist/components/queue.js.map +1 -0
  27. package/dist/components/rate-limiter.d.ts +24 -0
  28. package/dist/components/rate-limiter.d.ts.map +1 -0
  29. package/dist/components/rate-limiter.js +77 -0
  30. package/dist/components/rate-limiter.js.map +1 -0
  31. package/dist/components/registry.d.ts +19 -0
  32. package/dist/components/registry.d.ts.map +1 -0
  33. package/dist/components/registry.js +43 -0
  34. package/dist/components/registry.js.map +1 -0
  35. package/dist/components/retry-policy.d.ts +41 -0
  36. package/dist/components/retry-policy.d.ts.map +1 -0
  37. package/dist/components/retry-policy.js +122 -0
  38. package/dist/components/retry-policy.js.map +1 -0
  39. package/dist/components/server.d.ts +26 -0
  40. package/dist/components/server.d.ts.map +1 -0
  41. package/dist/components/server.js +82 -0
  42. package/dist/components/server.js.map +1 -0
  43. package/dist/components/sink.d.ts +12 -0
  44. package/dist/components/sink.d.ts.map +1 -0
  45. package/dist/components/sink.js +35 -0
  46. package/dist/components/sink.js.map +1 -0
  47. package/dist/components/worker-pool.d.ts +68 -0
  48. package/dist/components/worker-pool.d.ts.map +1 -0
  49. package/dist/components/worker-pool.js +224 -0
  50. package/dist/components/worker-pool.js.map +1 -0
  51. package/dist/format/definition.d.ts +72 -0
  52. package/dist/format/definition.d.ts.map +1 -0
  53. package/dist/format/definition.js +17 -0
  54. package/dist/format/definition.js.map +1 -0
  55. package/dist/format/index.d.ts +5 -0
  56. package/dist/format/index.d.ts.map +1 -0
  57. package/dist/format/index.js +3 -0
  58. package/dist/format/index.js.map +1 -0
  59. package/dist/format/loader.d.ts +19 -0
  60. package/dist/format/loader.d.ts.map +1 -0
  61. package/dist/format/loader.js +275 -0
  62. package/dist/format/loader.js.map +1 -0
  63. package/dist/index.d.ts +9 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +9 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/kernel/heap.d.ts +14 -0
  68. package/dist/kernel/heap.d.ts.map +1 -0
  69. package/dist/kernel/heap.js +55 -0
  70. package/dist/kernel/heap.js.map +1 -0
  71. package/dist/kernel/index.d.ts +3 -0
  72. package/dist/kernel/index.d.ts.map +1 -0
  73. package/dist/kernel/index.js +2 -0
  74. package/dist/kernel/index.js.map +1 -0
  75. package/dist/kernel/kernel.d.ts +104 -0
  76. package/dist/kernel/kernel.d.ts.map +1 -0
  77. package/dist/kernel/kernel.js +236 -0
  78. package/dist/kernel/kernel.js.map +1 -0
  79. package/dist/model/entity.d.ts +78 -0
  80. package/dist/model/entity.d.ts.map +1 -0
  81. package/dist/model/entity.js +108 -0
  82. package/dist/model/entity.js.map +1 -0
  83. package/dist/model/fifo.d.ts +9 -0
  84. package/dist/model/fifo.d.ts.map +1 -0
  85. package/dist/model/fifo.js +24 -0
  86. package/dist/model/fifo.js.map +1 -0
  87. package/dist/model/index.d.ts +4 -0
  88. package/dist/model/index.d.ts.map +1 -0
  89. package/dist/model/index.js +3 -0
  90. package/dist/model/index.js.map +1 -0
  91. package/dist/rng/distributions.d.ts +98 -0
  92. package/dist/rng/distributions.d.ts.map +1 -0
  93. package/dist/rng/distributions.js +224 -0
  94. package/dist/rng/distributions.js.map +1 -0
  95. package/dist/rng/index.d.ts +4 -0
  96. package/dist/rng/index.d.ts.map +1 -0
  97. package/dist/rng/index.js +3 -0
  98. package/dist/rng/index.js.map +1 -0
  99. package/dist/rng/rng.d.ts +26 -0
  100. package/dist/rng/rng.d.ts.map +1 -0
  101. package/dist/rng/rng.js +101 -0
  102. package/dist/rng/rng.js.map +1 -0
  103. package/dist/run/assertions.d.ts +9 -0
  104. package/dist/run/assertions.d.ts.map +1 -0
  105. package/dist/run/assertions.js +57 -0
  106. package/dist/run/assertions.js.map +1 -0
  107. package/dist/run/index.d.ts +6 -0
  108. package/dist/run/index.d.ts.map +1 -0
  109. package/dist/run/index.js +4 -0
  110. package/dist/run/index.js.map +1 -0
  111. package/dist/run/results.d.ts +72 -0
  112. package/dist/run/results.d.ts.map +1 -0
  113. package/dist/run/results.js +2 -0
  114. package/dist/run/results.js.map +1 -0
  115. package/dist/run/run.d.ts +29 -0
  116. package/dist/run/run.d.ts.map +1 -0
  117. package/dist/run/run.js +80 -0
  118. package/dist/run/run.js.map +1 -0
  119. package/dist/run/simulation.d.ts +34 -0
  120. package/dist/run/simulation.d.ts.map +1 -0
  121. package/dist/run/simulation.js +104 -0
  122. package/dist/run/simulation.js.map +1 -0
  123. package/dist/schema/index.d.ts +5 -0
  124. package/dist/schema/index.d.ts.map +1 -0
  125. package/dist/schema/index.js +3 -0
  126. package/dist/schema/index.js.map +1 -0
  127. package/dist/schema/types.d.ts +83 -0
  128. package/dist/schema/types.d.ts.map +1 -0
  129. package/dist/schema/types.js +19 -0
  130. package/dist/schema/types.js.map +1 -0
  131. package/dist/schema/validate.d.ts +19 -0
  132. package/dist/schema/validate.d.ts.map +1 -0
  133. package/dist/schema/validate.js +120 -0
  134. package/dist/schema/validate.js.map +1 -0
  135. package/dist/stats/index.d.ts +6 -0
  136. package/dist/stats/index.d.ts.map +1 -0
  137. package/dist/stats/index.js +4 -0
  138. package/dist/stats/index.js.map +1 -0
  139. package/dist/stats/percentile.d.ts +38 -0
  140. package/dist/stats/percentile.d.ts.map +1 -0
  141. package/dist/stats/percentile.js +62 -0
  142. package/dist/stats/percentile.js.map +1 -0
  143. package/dist/stats/summary.d.ts +20 -0
  144. package/dist/stats/summary.d.ts.map +1 -0
  145. package/dist/stats/summary.js +38 -0
  146. package/dist/stats/summary.js.map +1 -0
  147. package/dist/stats/time-weighted.d.ts +31 -0
  148. package/dist/stats/time-weighted.d.ts.map +1 -0
  149. package/dist/stats/time-weighted.js +69 -0
  150. package/dist/stats/time-weighted.js.map +1 -0
  151. package/package.json +41 -0
@@ -0,0 +1,122 @@
1
+ import { Priority } from "../kernel/index.js";
2
+ import { LinkedComponent } from "../model/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 class RetryPolicy extends LinkedComponent {
19
+ static schema = {
20
+ type: "RetryPolicy",
21
+ description: "Retries failed attempts with exponential backoff and jitter, then gives up.",
22
+ roles: ["receiver"],
23
+ inputs: [
24
+ { key: "maxAttempts", type: "integer", unit: "dimensionless", min: 1, default: 3, required: false, description: "Total attempts including the first." },
25
+ { key: "baseDelay", type: "number", unit: "time", min: 0, default: 0.1, required: false, description: "Delay before the first retry (seconds)." },
26
+ { key: "multiplier", type: "number", unit: "dimensionless", min: 1, default: 2, required: false, description: "Backoff growth factor per retry." },
27
+ { key: "maxDelay", type: "number", unit: "time", min: 0, required: false, description: "Cap on the delay before jitter (seconds). Uncapped if omitted." },
28
+ { key: "jitter", type: "enum", unit: "dimensionless", options: ["none", "full", "equal"], default: "full", required: false, description: "Randomisation of each delay." },
29
+ ],
30
+ links: [
31
+ { key: "next", description: "Where each attempt is sent (the thing that can fail).", required: true, accepts: "receiver" },
32
+ { key: "giveUp", description: "Where entities go after exhausting maxAttempts. Dropped and counted if omitted.", required: false, accepts: "receiver" },
33
+ ],
34
+ outputs: [
35
+ { key: "NumberRequests", unit: "dimensionless", description: "Fresh requests received.", get: (r) => r.requests },
36
+ { key: "NumberAttempts", unit: "dimensionless", description: "Attempts sent to next (first attempts plus retries).", get: (r) => r.attempts },
37
+ { key: "NumberRetries", unit: "dimensionless", description: "Retries scheduled.", get: (r) => r.retries },
38
+ { key: "NumberGivenUp", unit: "dimensionless", description: "Requests that exhausted maxAttempts.", get: (r) => r.givenUp },
39
+ { key: "RetryAmplification", unit: "dimensionless", description: "Attempts per fresh request: the load multiplier retries put on the downstream.", get: (r) => (r.requests === 0 ? NaN : r.attempts / r.requests) },
40
+ { key: "Retrying", unit: "dimensionless", series: true, description: "Requests currently waiting out a backoff delay.", get: (r) => r.waiting },
41
+ ],
42
+ };
43
+ attemptKey;
44
+ maxAttempts;
45
+ baseDelay;
46
+ multiplier;
47
+ maxDelay;
48
+ jitter;
49
+ unit;
50
+ giveUpTarget = null;
51
+ requests = 0;
52
+ attempts = 0;
53
+ retries = 0;
54
+ givenUp = 0;
55
+ waiting = 0;
56
+ constructor(ctx, init) {
57
+ super(ctx, init, "Idle");
58
+ const i = init.inputs;
59
+ this.attemptKey = `retry:${init.name}`;
60
+ this.maxAttempts = i["maxAttempts"];
61
+ this.baseDelay = i["baseDelay"];
62
+ this.multiplier = i["multiplier"];
63
+ this.maxDelay = i["maxDelay"] ?? Infinity;
64
+ this.jitter = i["jitter"];
65
+ this.unit = this.makeSampler({ dist: "uniform", min: 0, max: 1 }, "jitter");
66
+ }
67
+ setLink(key, target) {
68
+ if (key === "giveUp")
69
+ this.giveUpTarget = target;
70
+ else
71
+ super.setLink(key, target);
72
+ }
73
+ addEntity(entity) {
74
+ const made = entity.attributes[this.attemptKey];
75
+ if (made === undefined) {
76
+ this.requests++;
77
+ entity.attributes[this.attemptKey] = 1;
78
+ this.send(entity);
79
+ return;
80
+ }
81
+ // A failed attempt came back.
82
+ if (made >= this.maxAttempts) {
83
+ this.givenUp++;
84
+ this.giveUpTarget?.addEntity(entity);
85
+ return;
86
+ }
87
+ this.retries++;
88
+ this.waiting++;
89
+ this.setState("Backoff");
90
+ this.ctx.kernel.schedule(this.secondsToTicks(this.backoff(made)), Priority.DEFAULT, () => {
91
+ this.waiting--;
92
+ if (this.waiting === 0)
93
+ this.setState("Idle");
94
+ entity.attributes[this.attemptKey] = made + 1;
95
+ this.send(entity);
96
+ });
97
+ }
98
+ send(entity) {
99
+ this.attempts++;
100
+ this.next?.addEntity(entity);
101
+ }
102
+ /** Delay in seconds before the retry that follows `made` attempts. */
103
+ backoff(made) {
104
+ const d = Math.min(this.maxDelay, this.baseDelay * this.multiplier ** (made - 1));
105
+ switch (this.jitter) {
106
+ case "full":
107
+ return d * this.unit.nextSample();
108
+ case "equal":
109
+ return d / 2 + (d / 2) * this.unit.nextSample();
110
+ default:
111
+ return d;
112
+ }
113
+ }
114
+ resetStatistics() {
115
+ super.resetStatistics();
116
+ this.requests = 0;
117
+ this.attempts = 0;
118
+ this.retries = 0;
119
+ this.givenUp = 0;
120
+ }
121
+ }
122
+ //# sourceMappingURL=retry-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-policy.js","sourceRoot":"","sources":["../../src/components/retry-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAA0D,MAAM,mBAAmB,CAAC;AAI5G;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,WAAY,SAAQ,eAAe;IAC9C,MAAM,CAAU,MAAM,GAAiC;QACrD,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,6EAA6E;QAC1F,KAAK,EAAE,CAAC,UAAU,CAAC;QACnB,MAAM,EAAE;YACN,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,qCAAqC,EAAE;YACvJ,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,yCAAyC,EAAE;YACjJ,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,kCAAkC,EAAE;YAClJ,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,gEAAgE,EAAE;YACzJ,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,8BAA8B,EAAE;SAC1K;QACD,KAAK,EAAE;YACL,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,uDAAuD,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE;YAC1H,EAAE,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,iFAAiF,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE;SACxJ;QACD,OAAO,EAAE;YACP,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,0BAA0B,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;YACjH,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,sDAAsD,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;YAC7I,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE;YACzG,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,sCAAsC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE;YAC3H,EAAE,GAAG,EAAE,oBAAoB,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,gFAAgF,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE;YACnN,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,iDAAiD,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE;SAChJ;KACF,CAAC;IAEe,UAAU,CAAS;IACnB,WAAW,CAAS;IACpB,SAAS,CAAS;IAClB,UAAU,CAAS;IACnB,QAAQ,CAAS;IACjB,MAAM,CAAS;IACf,IAAI,CAAiB;IAC9B,YAAY,GAA2B,IAAI,CAAC;IAC5C,QAAQ,GAAG,CAAC,CAAC;IACb,QAAQ,GAAG,CAAC,CAAC;IACb,OAAO,GAAG,CAAC,CAAC;IACZ,OAAO,GAAG,CAAC,CAAC;IACZ,OAAO,GAAG,CAAC,CAAC;IAEpB,YAAY,GAAe,EAAE,IAAmB;QAC9C,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACzB,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QACtB,IAAI,CAAC,UAAU,GAAG,SAAS,IAAI,CAAC,IAAI,EAAE,CAAC;QACvC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,aAAa,CAAW,CAAC;QAC9C,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,WAAW,CAAW,CAAC;QAC1C,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,YAAY,CAAW,CAAC;QAC5C,IAAI,CAAC,QAAQ,GAAI,CAAC,CAAC,UAAU,CAAwB,IAAI,QAAQ,CAAC;QAClE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAW,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC9E,CAAC;IAEQ,OAAO,CAAC,GAAW,EAAE,MAAuB;QACnD,IAAI,GAAG,KAAK,QAAQ;YAAE,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;;YAC5C,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;IAEQ,SAAS,CAAC,MAAoB;QACrC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAuB,CAAC;QACtE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACvC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAClB,OAAO;QACT,CAAC;QACD,8BAA8B;QAC9B,IAAI,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;YACrC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;YACvF,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC;gBAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC9C,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;YAC9C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,IAAI,CAAC,MAAoB;QAC/B,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED,sEAAsE;IAC9D,OAAO,CAAC,IAAY;QAC1B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;QAClF,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;YACpB,KAAK,MAAM;gBACT,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACpC,KAAK,OAAO;gBACV,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YAClD;gBACE,OAAO,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IAEQ,eAAe;QACtB,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;IACnB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { LinkedComponent, type ComponentInit, type SimContext } from "../model/index.js";
2
+ import { type ComponentSchema } from "../schema/index.js";
3
+ import type { QueueWaiter } from "./pullable.js";
4
+ /**
5
+ * A pool of `capacity` parallel workers pulling from a Queue. A worker that starts an entity
6
+ * schedules `endStep` after the sampled service time; on endStep it sends the entity to `next`
7
+ * and pulls the next entity, or goes idle.
8
+ */
9
+ export declare class Server extends LinkedComponent implements QueueWaiter {
10
+ static readonly schema: ComponentSchema<Server>;
11
+ private readonly capacity;
12
+ private readonly serviceTime;
13
+ private readonly busyStat;
14
+ private queue;
15
+ private busy;
16
+ constructor(ctx: SimContext, init: ComponentInit);
17
+ private get now();
18
+ setLink(key: string, target: LinkedComponent): void;
19
+ queueHasEntity(): void;
20
+ /** Pull one entity from the queue onto a worker. Returns false if the queue was empty. */
21
+ private startNext;
22
+ private endStep;
23
+ private setBusy;
24
+ resetStatistics(): void;
25
+ }
26
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/components/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAqB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE5G,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD;;;;GAIG;AACH,qBAAa,MAAO,SAAQ,eAAgB,YAAW,WAAW;IAChE,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,CAiB7C;IAEF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiB;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,IAAI,CAAK;gBAEL,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa;IAOhD,OAAO,KAAK,GAAG,GAEd;IAEQ,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAS5D,cAAc,IAAI,IAAI;IAMtB,0FAA0F;IAC1F,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,OAAO;IAMN,eAAe,IAAI,IAAI;CAIjC"}
@@ -0,0 +1,82 @@
1
+ import { Priority } from "../kernel/index.js";
2
+ import { LinkedComponent } from "../model/index.js";
3
+ import { TimeWeightedStat } from "../stats/index.js";
4
+ import { asSampler } from "../schema/index.js";
5
+ /**
6
+ * A pool of `capacity` parallel workers pulling from a Queue. A worker that starts an entity
7
+ * schedules `endStep` after the sampled service time; on endStep it sends the entity to `next`
8
+ * and pulls the next entity, or goes idle.
9
+ */
10
+ export class Server extends LinkedComponent {
11
+ static schema = {
12
+ type: "Server",
13
+ description: "Pool of parallel workers that pull entities from a queue, serve them, and pass them on.",
14
+ roles: [],
15
+ inputs: [
16
+ { key: "capacity", type: "integer", unit: "dimensionless", min: 1, default: 1, required: false, description: "Number of parallel workers." },
17
+ { key: "serviceTime", type: "sampler", unit: "time", min: 0, required: true, description: "Service time per entity (seconds)." },
18
+ ],
19
+ links: [
20
+ { key: "queue", description: "Queue this server pulls from.", required: true, accepts: "queue" },
21
+ { key: "next", description: "Where served entities are sent.", required: false, accepts: "receiver" },
22
+ ],
23
+ outputs: [
24
+ { key: "Utilisation", unit: "dimensionless", description: "Time-weighted busy workers / capacity.", get: (s) => s.busyStat.mean(s.now) / s.capacity },
25
+ { key: "AverageBusyWorkers", unit: "dimensionless", description: "Time-weighted average number of busy workers.", get: (s) => s.busyStat.mean(s.now) },
26
+ { key: "BusyWorkers", unit: "dimensionless", series: true, description: "Workers currently busy.", get: (s) => s.busy },
27
+ ],
28
+ };
29
+ capacity;
30
+ serviceTime;
31
+ busyStat;
32
+ queue = null;
33
+ busy = 0;
34
+ constructor(ctx, init) {
35
+ super(ctx, init, "Idle");
36
+ this.capacity = init.inputs["capacity"];
37
+ this.serviceTime = this.makeSampler(asSampler(init.inputs["serviceTime"]), "serviceTime");
38
+ this.busyStat = new TimeWeightedStat(ctx.kernel.currentTick, 0);
39
+ }
40
+ get now() {
41
+ return this.ctx.kernel.currentTick;
42
+ }
43
+ setLink(key, target) {
44
+ if (key === "queue") {
45
+ this.queue = target;
46
+ this.queue.addWaiter(this);
47
+ }
48
+ else {
49
+ super.setLink(key, target);
50
+ }
51
+ }
52
+ queueHasEntity() {
53
+ while (this.busy < this.capacity && this.startNext()) {
54
+ /* fill idle workers */
55
+ }
56
+ }
57
+ /** Pull one entity from the queue onto a worker. Returns false if the queue was empty. */
58
+ startNext() {
59
+ const entity = this.queue?.poll() ?? null;
60
+ if (entity === null)
61
+ return false;
62
+ this.noteAdded();
63
+ this.setBusy(this.busy + 1);
64
+ this.ctx.kernel.schedule(this.secondsToTicks(this.serviceTime.nextSample()), Priority.DEFAULT, () => this.endStep(entity));
65
+ return true;
66
+ }
67
+ endStep(entity) {
68
+ this.setBusy(this.busy - 1);
69
+ this.sendToNext(entity);
70
+ this.queueHasEntity();
71
+ }
72
+ setBusy(n) {
73
+ this.busy = n;
74
+ this.busyStat.set(this.now, n);
75
+ this.setState(n === 0 ? "Idle" : "Busy");
76
+ }
77
+ resetStatistics() {
78
+ super.resetStatistics();
79
+ this.busyStat.reset(this.now);
80
+ }
81
+ }
82
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/components/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAA0D,MAAM,mBAAmB,CAAC;AAC5G,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAwB,MAAM,oBAAoB,CAAC;AAKrE;;;;GAIG;AACH,MAAM,OAAO,MAAO,SAAQ,eAAe;IACzC,MAAM,CAAU,MAAM,GAA4B;QAChD,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,yFAAyF;QACtG,KAAK,EAAE,EAAE;QACT,MAAM,EAAE;YACN,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,6BAA6B,EAAE;YAC5I,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oCAAoC,EAAE;SACjI;QACD,KAAK,EAAE;YACL,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,+BAA+B,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE;YAChG,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,iCAAiC,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE;SACtG;QACD,OAAO,EAAE;YACP,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,wCAAwC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;YACrJ,EAAE,GAAG,EAAE,oBAAoB,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,+CAA+C,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE;YACtJ,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,yBAAyB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE;SACxH;KACF,CAAC;IAEe,QAAQ,CAAS;IACjB,WAAW,CAAiB;IAC5B,QAAQ,CAAmB;IACpC,KAAK,GAAiB,IAAI,CAAC;IAC3B,IAAI,GAAG,CAAC,CAAC;IAEjB,YAAY,GAAe,EAAE,IAAmB;QAC9C,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAW,CAAC;QAClD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;QAC1F,IAAI,CAAC,QAAQ,GAAG,IAAI,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,IAAY,GAAG;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;IACrC,CAAC;IAEQ,OAAO,CAAC,GAAW,EAAE,MAAuB;QACnD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;YACpB,IAAI,CAAC,KAAK,GAAG,MAAe,CAAC;YAC7B,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACrD,uBAAuB;QACzB,CAAC;IACH,CAAC;IAED,0FAA0F;IAClF,SAAS;QACf,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;QAC1C,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QAClC,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,EAAE,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,CAClG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CACrB,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,OAAO,CAAC,MAAoB;QAClC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACxB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IAEO,OAAO,CAAC,CAAS;QACvB,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACd,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAEQ,eAAe;QACtB,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { LinkedComponent, type ComponentInit, type MovingEntity, type SimContext } from "../model/index.js";
2
+ import type { ComponentSchema } from "../schema/index.js";
3
+ /** Consumes entities and records each one's time in system (now - creation time). */
4
+ export declare class EntitySink extends LinkedComponent {
5
+ static readonly schema: ComponentSchema<EntitySink>;
6
+ /** Swappable for an approximate streaming sketch later. */
7
+ private readonly times;
8
+ constructor(ctx: SimContext, init: ComponentInit);
9
+ addEntity(entity: MovingEntity): void;
10
+ resetStatistics(): void;
11
+ }
12
+ //# sourceMappingURL=sink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sink.d.ts","sourceRoot":"","sources":["../../src/components/sink.ts"],"names":[],"mappings":"AAAA,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,qFAAqF;AACrF,qBAAa,UAAW,SAAQ,eAAe;IAC7C,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,UAAU,CAAC,CAajD;IAEF,2DAA2D;IAC3D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmD;gBAE7D,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa;IAIvC,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAOrC,eAAe,IAAI,IAAI;CAIjC"}
@@ -0,0 +1,35 @@
1
+ import { LinkedComponent } from "../model/index.js";
2
+ import { ExactPercentileTracker } from "../stats/index.js";
3
+ /** Consumes entities and records each one's time in system (now - creation time). */
4
+ export class EntitySink extends LinkedComponent {
5
+ static schema = {
6
+ type: "EntitySink",
7
+ description: "Consumes entities and records their time in system.",
8
+ roles: ["receiver"],
9
+ inputs: [],
10
+ links: [],
11
+ outputs: [
12
+ { key: "count", unit: "dimensionless", description: "Entities received.", get: (s) => s.times.count },
13
+ { key: "mean", unit: "time", description: "Mean time in system.", get: (s) => s.times.mean() },
14
+ { key: "p50", unit: "time", description: "Median time in system.", get: (s) => s.times.percentile(50) },
15
+ { key: "p95", unit: "time", description: "95th percentile time in system.", get: (s) => s.times.percentile(95) },
16
+ { key: "p99", unit: "time", description: "99th percentile time in system.", get: (s) => s.times.percentile(99) },
17
+ ],
18
+ };
19
+ /** Swappable for an approximate streaming sketch later. */
20
+ times = new ExactPercentileTracker();
21
+ constructor(ctx, init) {
22
+ super(ctx, init, "Idle");
23
+ }
24
+ addEntity(entity) {
25
+ this.noteAdded();
26
+ const k = this.ctx.kernel;
27
+ this.times.add(k.ticksToSeconds(k.currentTick - entity.createdAt));
28
+ this.noteCompleted();
29
+ }
30
+ resetStatistics() {
31
+ super.resetStatistics();
32
+ this.times.reset();
33
+ }
34
+ }
35
+ //# sourceMappingURL=sink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sink.js","sourceRoot":"","sources":["../../src/components/sink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAA0D,MAAM,mBAAmB,CAAC;AAC5G,OAAO,EAAE,sBAAsB,EAA0B,MAAM,mBAAmB,CAAC;AAGnF,qFAAqF;AACrF,MAAM,OAAO,UAAW,SAAQ,eAAe;IAC7C,MAAM,CAAU,MAAM,GAAgC;QACpD,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,qDAAqD;QAClE,KAAK,EAAE,CAAC,UAAU,CAAC;QACnB,MAAM,EAAE,EAAE;QACV,KAAK,EAAE,EAAE;QACT,OAAO,EAAE;YACP,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE;YACrG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,sBAAsB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE;YAC9F,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,wBAAwB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE;YACvG,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,iCAAiC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE;YAChH,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,iCAAiC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE;SACjH;KACF,CAAC;IAEF,2DAA2D;IAC1C,KAAK,GAAsB,IAAI,sBAAsB,EAAE,CAAC;IAEzE,YAAY,GAAe,EAAE,IAAmB;QAC9C,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC;IAEQ,SAAS,CAAC,MAAoB;QACrC,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC;IAEQ,eAAe;QACtB,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC"}
@@ -0,0 +1,68 @@
1
+ import { LinkedComponent, type ComponentInit, type MovingEntity, type SimContext } from "../model/index.js";
2
+ import { type ComponentSchema } from "../schema/index.js";
3
+ import type { Scalable } from "./autoscaler.js";
4
+ import type { QueueWaiter } from "./pullable.js";
5
+ /**
6
+ * A pool of up to `concurrency` workers, modelled on a serverless function or container fleet.
7
+ *
8
+ * - **Pull mode** (`queue` link): workers pull from a Queue or MessageQueue whenever one is free.
9
+ * - **Push mode** (entities sent straight to the pool): when all `concurrency` workers are busy the
10
+ * request is *throttled* (rejected) and goes to the `onThrottle` link, or is dropped and counted.
11
+ * - **Cold starts**: a worker needs a warm instance. If none is idle, a new one starts and the
12
+ * request pays `coldStartTime` on top of its service time. Instances idle for `idleTimeout`
13
+ * seconds are reclaimed; the most recently used instance is reused first.
14
+ * - **Failures**: after service, an entity fails with probability `failureProbability`. A failure
15
+ * goes to `onFailure` if linked (and is acked). Otherwise, with a MessageQueue source it is left
16
+ * unacked so the visibility timeout redelivers it; with a plain Queue or push mode it is lost.
17
+ */
18
+ export declare class WorkerPool extends LinkedComponent implements QueueWaiter, Scalable {
19
+ static readonly schema: ComponentSchema<WorkerPool>;
20
+ private limit;
21
+ private readonly prices;
22
+ private readonly serviceTime;
23
+ private readonly coldStart;
24
+ private readonly failureDraw;
25
+ private readonly failureProbability;
26
+ private readonly idleTimeoutTicks;
27
+ private readonly busyStat;
28
+ private readonly limitStat;
29
+ /** Busy workers since time 0; never reset, so an Autoscaler can read deltas across the warm-up reset. */
30
+ private readonly busyLifetime;
31
+ /** Tick at which each idle warm instance became idle, oldest first. */
32
+ private readonly idle;
33
+ private source;
34
+ private onFailure;
35
+ private onThrottle;
36
+ private busy;
37
+ private coldStarts;
38
+ private succeeded;
39
+ private failed;
40
+ private throttled;
41
+ private pushed;
42
+ private staleAcks;
43
+ private started;
44
+ constructor(ctx: SimContext, init: ComponentInit);
45
+ private get now();
46
+ /** Current concurrency limit (see {@link Scalable}). */
47
+ get concurrency(): number;
48
+ /**
49
+ * Change the concurrency limit. Raising it starts queued work immediately; lowering it never
50
+ * interrupts running work, the pool just stops starting new work until busy workers drop below it.
51
+ */
52
+ setConcurrency(n: number): void;
53
+ /** Cumulative busy-worker-ticks since time 0 (not affected by the warm-up reset). */
54
+ busyWorkerTicks(): number;
55
+ private cost;
56
+ setLink(key: string, target: LinkedComponent): void;
57
+ /** Push mode: start the entity if a worker is free, otherwise throttle it. */
58
+ addEntity(entity: MovingEntity): void;
59
+ /** Pull mode: fill free workers from the queue. */
60
+ queueHasEntity(): void;
61
+ private begin;
62
+ /** Reclaim expired idle instances, then take the most recently used one. False if none is warm. */
63
+ private takeWarmInstance;
64
+ private end;
65
+ private setBusy;
66
+ resetStatistics(): void;
67
+ }
68
+ //# sourceMappingURL=worker-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../src/components/worker-pool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE5G,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAqB,WAAW,EAAE,MAAM,eAAe,CAAC;AAEpE;;;;;;;;;;;;GAYG;AACH,qBAAa,UAAW,SAAQ,eAAgB,YAAW,WAAW,EAAE,QAAQ;IAC9E,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,UAAU,CAAC,CAoCjD;IAEF,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyD;IAChF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiB;IAC7C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,yGAAyG;IACzG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmB;IAChD,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,OAAO,CAAK;gBAER,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa;IAsBhD,OAAO,KAAK,GAAG,GAEd;IAED,wDAAwD;IACxD,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;;OAGG;IACH,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAM/B,qFAAqF;IACrF,eAAe,IAAI,MAAM;IAIzB,OAAO,CAAC,IAAI;IAOH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAiB5D,8EAA8E;IACrE,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAU9C,mDAAmD;IACnD,cAAc,IAAI,IAAI;IAQtB,OAAO,CAAC,KAAK;IAYb,mGAAmG;IACnG,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,GAAG;IAoBX,OAAO,CAAC,OAAO;IAON,eAAe,IAAI,IAAI;CAYjC"}
@@ -0,0 +1,224 @@
1
+ import { Priority } from "../kernel/index.js";
2
+ import { LinkedComponent } from "../model/index.js";
3
+ import { asSampler } from "../schema/index.js";
4
+ import { TimeWeightedStat } from "../stats/index.js";
5
+ /**
6
+ * A pool of up to `concurrency` workers, modelled on a serverless function or container fleet.
7
+ *
8
+ * - **Pull mode** (`queue` link): workers pull from a Queue or MessageQueue whenever one is free.
9
+ * - **Push mode** (entities sent straight to the pool): when all `concurrency` workers are busy the
10
+ * request is *throttled* (rejected) and goes to the `onThrottle` link, or is dropped and counted.
11
+ * - **Cold starts**: a worker needs a warm instance. If none is idle, a new one starts and the
12
+ * request pays `coldStartTime` on top of its service time. Instances idle for `idleTimeout`
13
+ * seconds are reclaimed; the most recently used instance is reused first.
14
+ * - **Failures**: after service, an entity fails with probability `failureProbability`. A failure
15
+ * goes to `onFailure` if linked (and is acked). Otherwise, with a MessageQueue source it is left
16
+ * unacked so the visibility timeout redelivers it; with a plain Queue or push mode it is lost.
17
+ */
18
+ export class WorkerPool extends LinkedComponent {
19
+ static schema = {
20
+ type: "WorkerPool",
21
+ description: "Worker pool with a concurrency limit, throttling, cold starts and failures. Pulls from a queue or accepts pushed entities.",
22
+ roles: ["receiver", "scalable"],
23
+ inputs: [
24
+ { key: "concurrency", type: "integer", unit: "dimensionless", min: 1, required: true, description: "Maximum workers busy at once (cold-starting workers count). An Autoscaler can change it during the run; this is the starting value." },
25
+ { key: "serviceTime", type: "sampler", unit: "time", min: 0, required: true, description: "Service time per entity (seconds)." },
26
+ { key: "coldStartTime", type: "sampler", unit: "time", min: 0, default: 0, required: false, description: "Extra seconds when a request has to start a new instance." },
27
+ { key: "idleTimeout", type: "number", unit: "time", min: 0, required: false, description: "Seconds an idle instance stays warm. Instances never expire if omitted." },
28
+ { key: "initialWarm", type: "integer", unit: "dimensionless", min: 0, default: 0, required: false, description: "Instances already warm at time 0 (capped at concurrency)." },
29
+ { key: "failureProbability", type: "number", unit: "dimensionless", min: 0, max: 1, default: 0, required: false, description: "Probability that an entity fails after its service time." },
30
+ { key: "costPerBusySecond", type: "number", unit: "cost", min: 0, default: 0, required: false, description: "Price per busy-worker-second (serverless-style billing)." },
31
+ { key: "costPerProvisionedSecond", type: "number", unit: "cost", min: 0, default: 0, required: false, description: "Price per provisioned-worker-second, busy or not (container/VM-style billing; follows autoscaling)." },
32
+ { key: "costPerRequest", type: "number", unit: "cost", min: 0, default: 0, required: false, description: "Price per request started." },
33
+ ],
34
+ links: [
35
+ { key: "queue", description: "Queue or MessageQueue to pull from (pull mode). Omit to accept pushed entities.", required: false, accepts: "pullable" },
36
+ { key: "next", description: "Where successfully served entities are sent.", required: false, accepts: "receiver" },
37
+ { key: "onFailure", description: "Where failed entities are sent (e.g. a RetryPolicy).", required: false, accepts: "receiver" },
38
+ { key: "onThrottle", description: "Where entities rejected because the pool was full are sent (e.g. a RetryPolicy).", required: false, accepts: "receiver" },
39
+ ],
40
+ outputs: [
41
+ { key: "Utilisation", unit: "dimensionless", description: "Time-weighted busy workers / provisioned concurrency.", get: (p) => p.busyStat.mean(p.now) / p.limitStat.mean(p.now) },
42
+ { key: "Concurrency", unit: "dimensionless", series: true, description: "Current concurrency limit (changes when autoscaled).", get: (p) => p.limit },
43
+ { key: "AverageConcurrency", unit: "dimensionless", description: "Time-weighted average concurrency limit.", get: (p) => p.limitStat.mean(p.now) },
44
+ { key: "AverageBusyWorkers", unit: "dimensionless", description: "Time-weighted average number of busy workers.", get: (p) => p.busyStat.mean(p.now) },
45
+ { key: "BusyWorkers", unit: "dimensionless", series: true, description: "Workers currently busy.", get: (p) => p.busy },
46
+ { key: "ColdStarts", unit: "dimensionless", description: "Requests that had to start a new instance (no idle warm instance).", get: (p) => p.coldStarts },
47
+ { key: "NumberSucceeded", unit: "dimensionless", description: "Entities served successfully.", get: (p) => p.succeeded },
48
+ { key: "NumberFailed", unit: "dimensionless", description: "Entities that failed after service.", get: (p) => p.failed },
49
+ { key: "NumberThrottled", unit: "dimensionless", description: "Pushed entities rejected because every worker was busy.", get: (p) => p.throttled },
50
+ { key: "ThrottleFraction", unit: "dimensionless", description: "Throttled / pushed entities (the blocking probability).", get: (p) => (p.pushed === 0 ? NaN : p.throttled / p.pushed) },
51
+ { key: "Cost", unit: "cost", description: "Busy-second, provisioned-second and per-request charges over the measured window.", get: (p) => p.cost() },
52
+ { key: "StaleAcks", unit: "dimensionless", description: "Successes whose acknowledgement was too late (visibility timeout had expired): duplicate processing.", get: (p) => p.staleAcks },
53
+ ],
54
+ };
55
+ limit;
56
+ prices;
57
+ serviceTime;
58
+ coldStart;
59
+ failureDraw;
60
+ failureProbability;
61
+ idleTimeoutTicks;
62
+ busyStat;
63
+ limitStat;
64
+ /** Busy workers since time 0; never reset, so an Autoscaler can read deltas across the warm-up reset. */
65
+ busyLifetime;
66
+ /** Tick at which each idle warm instance became idle, oldest first. */
67
+ idle = [];
68
+ source = null;
69
+ onFailure = null;
70
+ onThrottle = null;
71
+ busy = 0;
72
+ coldStarts = 0;
73
+ succeeded = 0;
74
+ failed = 0;
75
+ throttled = 0;
76
+ pushed = 0;
77
+ staleAcks = 0;
78
+ started = 0;
79
+ constructor(ctx, init) {
80
+ super(ctx, init, "Idle");
81
+ const i = init.inputs;
82
+ this.limit = i["concurrency"];
83
+ this.prices = {
84
+ busy: i["costPerBusySecond"],
85
+ provisioned: i["costPerProvisionedSecond"],
86
+ request: i["costPerRequest"],
87
+ };
88
+ this.serviceTime = this.makeSampler(asSampler(i["serviceTime"]), "serviceTime");
89
+ this.coldStart = this.makeSampler(asSampler(i["coldStartTime"]), "coldStartTime");
90
+ this.failureDraw = this.makeSampler({ dist: "uniform", min: 0, max: 1 }, "failure");
91
+ this.failureProbability = i["failureProbability"];
92
+ const timeout = i["idleTimeout"];
93
+ this.idleTimeoutTicks = timeout === undefined ? undefined : ctx.kernel.secondsToTicks(timeout);
94
+ this.busyStat = new TimeWeightedStat(ctx.kernel.currentTick, 0);
95
+ this.busyLifetime = new TimeWeightedStat(ctx.kernel.currentTick, 0);
96
+ this.limitStat = new TimeWeightedStat(ctx.kernel.currentTick, this.limit);
97
+ const warm = Math.min(i["initialWarm"], this.limit);
98
+ for (let w = 0; w < warm; w++)
99
+ this.idle.push(ctx.kernel.currentTick);
100
+ }
101
+ get now() {
102
+ return this.ctx.kernel.currentTick;
103
+ }
104
+ /** Current concurrency limit (see {@link Scalable}). */
105
+ get concurrency() {
106
+ return this.limit;
107
+ }
108
+ /**
109
+ * Change the concurrency limit. Raising it starts queued work immediately; lowering it never
110
+ * interrupts running work, the pool just stops starting new work until busy workers drop below it.
111
+ */
112
+ setConcurrency(n) {
113
+ this.limit = Math.max(1, Math.floor(n));
114
+ this.limitStat.set(this.now, this.limit);
115
+ this.queueHasEntity();
116
+ }
117
+ /** Cumulative busy-worker-ticks since time 0 (not affected by the warm-up reset). */
118
+ busyWorkerTicks() {
119
+ return this.busyLifetime.integral(this.now);
120
+ }
121
+ cost() {
122
+ const tps = this.ctx.kernel.ticksPerSecond;
123
+ const busySeconds = this.busyStat.integral(this.now) / tps;
124
+ const provisionedSeconds = this.limitStat.integral(this.now) / tps;
125
+ return busySeconds * this.prices.busy + provisionedSeconds * this.prices.provisioned + this.started * this.prices.request;
126
+ }
127
+ setLink(key, target) {
128
+ switch (key) {
129
+ case "queue":
130
+ this.source = target;
131
+ this.source.addWaiter(this);
132
+ break;
133
+ case "onFailure":
134
+ this.onFailure = target;
135
+ break;
136
+ case "onThrottle":
137
+ this.onThrottle = target;
138
+ break;
139
+ default:
140
+ super.setLink(key, target);
141
+ }
142
+ }
143
+ /** Push mode: start the entity if a worker is free, otherwise throttle it. */
144
+ addEntity(entity) {
145
+ this.pushed++;
146
+ if (this.busy >= this.limit) {
147
+ this.throttled++;
148
+ this.onThrottle?.addEntity(entity);
149
+ return;
150
+ }
151
+ this.begin(entity, null);
152
+ }
153
+ /** Pull mode: fill free workers from the queue. */
154
+ queueHasEntity() {
155
+ while (this.source !== null && this.busy < this.limit) {
156
+ const lease = this.source.receive();
157
+ if (lease === null)
158
+ break;
159
+ this.begin(lease.entity, lease);
160
+ }
161
+ }
162
+ begin(entity, lease) {
163
+ this.noteAdded();
164
+ this.started++;
165
+ this.setBusy(this.busy + 1);
166
+ let seconds = Math.max(0, this.serviceTime.nextSample());
167
+ if (!this.takeWarmInstance()) {
168
+ this.coldStarts++;
169
+ seconds += Math.max(0, this.coldStart.nextSample());
170
+ }
171
+ this.ctx.kernel.schedule(this.secondsToTicks(seconds), Priority.DEFAULT, () => this.end(entity, lease));
172
+ }
173
+ /** Reclaim expired idle instances, then take the most recently used one. False if none is warm. */
174
+ takeWarmInstance() {
175
+ if (this.idleTimeoutTicks !== undefined) {
176
+ let expired = 0;
177
+ while (expired < this.idle.length && this.now - this.idle[expired] >= this.idleTimeoutTicks)
178
+ expired++;
179
+ if (expired > 0)
180
+ this.idle.splice(0, expired);
181
+ }
182
+ return this.idle.pop() !== undefined;
183
+ }
184
+ end(entity, lease) {
185
+ this.idle.push(this.now);
186
+ this.setBusy(this.busy - 1);
187
+ const failed = this.failureProbability > 0 && this.failureDraw.nextSample() < this.failureProbability;
188
+ if (!failed) {
189
+ this.succeeded++;
190
+ if (lease !== null && !lease.ack())
191
+ this.staleAcks++;
192
+ this.sendToNext(entity);
193
+ }
194
+ else {
195
+ this.failed++;
196
+ this.noteCompleted();
197
+ if (this.onFailure !== null) {
198
+ lease?.ack();
199
+ this.onFailure.addEntity(entity);
200
+ }
201
+ // Otherwise: a MessageQueue redelivers after its visibility timeout; anything else is lost.
202
+ }
203
+ this.queueHasEntity();
204
+ }
205
+ setBusy(n) {
206
+ this.busy = n;
207
+ this.busyStat.set(this.now, n);
208
+ this.busyLifetime.set(this.now, n);
209
+ this.setState(n === 0 ? "Idle" : "Busy");
210
+ }
211
+ resetStatistics() {
212
+ super.resetStatistics();
213
+ this.busyStat.reset(this.now);
214
+ this.limitStat.reset(this.now);
215
+ this.started = 0;
216
+ this.coldStarts = 0;
217
+ this.succeeded = 0;
218
+ this.failed = 0;
219
+ this.throttled = 0;
220
+ this.pushed = 0;
221
+ this.staleAcks = 0;
222
+ }
223
+ }
224
+ //# sourceMappingURL=worker-pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-pool.js","sourceRoot":"","sources":["../../src/components/worker-pool.ts"],"names":[],"mappings":"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"}