@effect/cluster 0.50.6 → 0.52.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 (232) hide show
  1. package/RunnerStorage/package.json +6 -0
  2. package/SqlRunnerStorage/package.json +6 -0
  3. package/dist/cjs/ClusterError.js +2 -24
  4. package/dist/cjs/ClusterError.js.map +1 -1
  5. package/dist/cjs/ClusterMetrics.js +13 -15
  6. package/dist/cjs/ClusterMetrics.js.map +1 -1
  7. package/dist/cjs/ClusterSchema.js +17 -2
  8. package/dist/cjs/ClusterSchema.js.map +1 -1
  9. package/dist/cjs/ClusterWorkflowEngine.js +50 -83
  10. package/dist/cjs/ClusterWorkflowEngine.js.map +1 -1
  11. package/dist/cjs/Entity.js +1 -13
  12. package/dist/cjs/Entity.js.map +1 -1
  13. package/dist/cjs/EntityAddress.js +9 -1
  14. package/dist/cjs/EntityAddress.js.map +1 -1
  15. package/dist/cjs/EntityId.js +7 -1
  16. package/dist/cjs/EntityId.js.map +1 -1
  17. package/dist/cjs/EntityProxy.js +1 -1
  18. package/dist/cjs/EntityProxy.js.map +1 -1
  19. package/dist/cjs/HttpRunner.js +69 -43
  20. package/dist/cjs/HttpRunner.js.map +1 -1
  21. package/dist/cjs/MessageStorage.js +64 -16
  22. package/dist/cjs/MessageStorage.js.map +1 -1
  23. package/dist/cjs/Runner.js +3 -3
  24. package/dist/cjs/Runner.js.map +1 -1
  25. package/dist/cjs/RunnerAddress.js +7 -0
  26. package/dist/cjs/RunnerAddress.js.map +1 -1
  27. package/dist/cjs/RunnerHealth.js +91 -32
  28. package/dist/cjs/RunnerHealth.js.map +1 -1
  29. package/dist/cjs/RunnerServer.js +38 -24
  30. package/dist/cjs/RunnerServer.js.map +1 -1
  31. package/dist/cjs/RunnerStorage.js +100 -0
  32. package/dist/cjs/RunnerStorage.js.map +1 -0
  33. package/dist/cjs/Runners.js +18 -22
  34. package/dist/cjs/Runners.js.map +1 -1
  35. package/dist/cjs/ShardId.js +17 -7
  36. package/dist/cjs/ShardId.js.map +1 -1
  37. package/dist/cjs/Sharding.js +444 -320
  38. package/dist/cjs/Sharding.js.map +1 -1
  39. package/dist/cjs/ShardingConfig.js +10 -14
  40. package/dist/cjs/ShardingConfig.js.map +1 -1
  41. package/dist/cjs/Snowflake.js +1 -1
  42. package/dist/cjs/SocketRunner.js +1 -1
  43. package/dist/cjs/SocketRunner.js.map +1 -1
  44. package/dist/cjs/SqlMessageStorage.js +22 -28
  45. package/dist/cjs/SqlMessageStorage.js.map +1 -1
  46. package/dist/cjs/SqlRunnerStorage.js +375 -0
  47. package/dist/cjs/SqlRunnerStorage.js.map +1 -0
  48. package/dist/cjs/index.js +5 -15
  49. package/dist/cjs/internal/entityManager.js +42 -23
  50. package/dist/cjs/internal/entityManager.js.map +1 -1
  51. package/dist/dts/ClusterError.d.ts +0 -22
  52. package/dist/dts/ClusterError.d.ts.map +1 -1
  53. package/dist/dts/ClusterMetrics.d.ts +4 -14
  54. package/dist/dts/ClusterMetrics.d.ts.map +1 -1
  55. package/dist/dts/ClusterSchema.d.ts +9 -1
  56. package/dist/dts/ClusterSchema.d.ts.map +1 -1
  57. package/dist/dts/ClusterWorkflowEngine.d.ts.map +1 -1
  58. package/dist/dts/Entity.d.ts +3 -14
  59. package/dist/dts/Entity.d.ts.map +1 -1
  60. package/dist/dts/EntityAddress.d.ts +11 -0
  61. package/dist/dts/EntityAddress.d.ts.map +1 -1
  62. package/dist/dts/EntityId.d.ts +5 -0
  63. package/dist/dts/EntityId.d.ts.map +1 -1
  64. package/dist/dts/EntityProxy.d.ts +5 -6
  65. package/dist/dts/EntityProxy.d.ts.map +1 -1
  66. package/dist/dts/HttpRunner.d.ts +48 -25
  67. package/dist/dts/HttpRunner.d.ts.map +1 -1
  68. package/dist/dts/MessageStorage.d.ts +13 -5
  69. package/dist/dts/MessageStorage.d.ts.map +1 -1
  70. package/dist/dts/Runner.d.ts +4 -4
  71. package/dist/dts/Runner.d.ts.map +1 -1
  72. package/dist/dts/RunnerAddress.d.ts +5 -0
  73. package/dist/dts/RunnerAddress.d.ts.map +1 -1
  74. package/dist/dts/RunnerHealth.d.ts +24 -16
  75. package/dist/dts/RunnerHealth.d.ts.map +1 -1
  76. package/dist/dts/RunnerServer.d.ts +5 -4
  77. package/dist/dts/RunnerServer.d.ts.map +1 -1
  78. package/dist/dts/{ShardStorage.d.ts → RunnerStorage.d.ts} +41 -54
  79. package/dist/dts/RunnerStorage.d.ts.map +1 -0
  80. package/dist/dts/Runners.d.ts +15 -11
  81. package/dist/dts/Runners.d.ts.map +1 -1
  82. package/dist/dts/ShardId.d.ts +1 -1
  83. package/dist/dts/ShardId.d.ts.map +1 -1
  84. package/dist/dts/Sharding.d.ts +20 -10
  85. package/dist/dts/Sharding.d.ts.map +1 -1
  86. package/dist/dts/ShardingConfig.d.ts +40 -14
  87. package/dist/dts/ShardingConfig.d.ts.map +1 -1
  88. package/dist/dts/SocketRunner.d.ts +4 -3
  89. package/dist/dts/SocketRunner.d.ts.map +1 -1
  90. package/dist/dts/SqlMessageStorage.d.ts +2 -3
  91. package/dist/dts/SqlMessageStorage.d.ts.map +1 -1
  92. package/dist/dts/SqlRunnerStorage.d.ts +40 -0
  93. package/dist/dts/SqlRunnerStorage.d.ts.map +1 -0
  94. package/dist/dts/index.d.ts +4 -24
  95. package/dist/dts/index.d.ts.map +1 -1
  96. package/dist/esm/ClusterError.js +0 -21
  97. package/dist/esm/ClusterError.js.map +1 -1
  98. package/dist/esm/ClusterMetrics.js +12 -14
  99. package/dist/esm/ClusterMetrics.js.map +1 -1
  100. package/dist/esm/ClusterSchema.js +17 -2
  101. package/dist/esm/ClusterSchema.js.map +1 -1
  102. package/dist/esm/ClusterWorkflowEngine.js +50 -83
  103. package/dist/esm/ClusterWorkflowEngine.js.map +1 -1
  104. package/dist/esm/Entity.js +0 -12
  105. package/dist/esm/Entity.js.map +1 -1
  106. package/dist/esm/EntityAddress.js +7 -0
  107. package/dist/esm/EntityAddress.js.map +1 -1
  108. package/dist/esm/EntityId.js +5 -0
  109. package/dist/esm/EntityId.js.map +1 -1
  110. package/dist/esm/EntityProxy.js +2 -2
  111. package/dist/esm/EntityProxy.js.map +1 -1
  112. package/dist/esm/HttpRunner.js +62 -39
  113. package/dist/esm/HttpRunner.js.map +1 -1
  114. package/dist/esm/MessageStorage.js +65 -17
  115. package/dist/esm/MessageStorage.js.map +1 -1
  116. package/dist/esm/Runner.js +3 -3
  117. package/dist/esm/Runner.js.map +1 -1
  118. package/dist/esm/RunnerAddress.js +7 -0
  119. package/dist/esm/RunnerAddress.js.map +1 -1
  120. package/dist/esm/RunnerHealth.js +88 -30
  121. package/dist/esm/RunnerHealth.js.map +1 -1
  122. package/dist/esm/RunnerServer.js +38 -24
  123. package/dist/esm/RunnerServer.js.map +1 -1
  124. package/dist/esm/RunnerStorage.js +90 -0
  125. package/dist/esm/RunnerStorage.js.map +1 -0
  126. package/dist/esm/Runners.js +19 -23
  127. package/dist/esm/Runners.js.map +1 -1
  128. package/dist/esm/ShardId.js +16 -6
  129. package/dist/esm/ShardId.js.map +1 -1
  130. package/dist/esm/Sharding.js +447 -323
  131. package/dist/esm/Sharding.js.map +1 -1
  132. package/dist/esm/ShardingConfig.js +10 -14
  133. package/dist/esm/ShardingConfig.js.map +1 -1
  134. package/dist/esm/Snowflake.js +1 -1
  135. package/dist/esm/SocketRunner.js +1 -1
  136. package/dist/esm/SocketRunner.js.map +1 -1
  137. package/dist/esm/SqlMessageStorage.js +22 -28
  138. package/dist/esm/SqlMessageStorage.js.map +1 -1
  139. package/dist/esm/SqlRunnerStorage.js +366 -0
  140. package/dist/esm/SqlRunnerStorage.js.map +1 -0
  141. package/dist/esm/index.js +4 -24
  142. package/dist/esm/index.js.map +1 -1
  143. package/dist/esm/internal/entityManager.js +41 -22
  144. package/dist/esm/internal/entityManager.js.map +1 -1
  145. package/package.json +20 -60
  146. package/src/ClusterError.ts +0 -24
  147. package/src/ClusterMetrics.ts +12 -16
  148. package/src/ClusterSchema.ts +17 -2
  149. package/src/ClusterWorkflowEngine.ts +48 -80
  150. package/src/Entity.ts +3 -21
  151. package/src/EntityAddress.ts +10 -0
  152. package/src/EntityId.ts +6 -0
  153. package/src/EntityProxy.ts +10 -10
  154. package/src/HttpRunner.ts +132 -67
  155. package/src/MessageStorage.ts +89 -24
  156. package/src/Runner.ts +4 -4
  157. package/src/RunnerAddress.ts +8 -0
  158. package/src/RunnerHealth.ts +119 -56
  159. package/src/RunnerServer.ts +64 -47
  160. package/src/RunnerStorage.ts +218 -0
  161. package/src/Runners.ts +32 -45
  162. package/src/ShardId.ts +14 -3
  163. package/src/Sharding.ts +561 -417
  164. package/src/ShardingConfig.ts +39 -31
  165. package/src/Snowflake.ts +1 -1
  166. package/src/SocketRunner.ts +6 -4
  167. package/src/SqlMessageStorage.ts +28 -30
  168. package/src/SqlRunnerStorage.ts +537 -0
  169. package/src/index.ts +4 -29
  170. package/src/internal/entityManager.ts +45 -29
  171. package/HttpCommon/package.json +0 -6
  172. package/HttpShardManager/package.json +0 -6
  173. package/ShardManager/package.json +0 -6
  174. package/ShardStorage/package.json +0 -6
  175. package/SocketShardManager/package.json +0 -6
  176. package/SqlShardStorage/package.json +0 -6
  177. package/SynchronizedClock/package.json +0 -6
  178. package/dist/cjs/HttpCommon.js +0 -48
  179. package/dist/cjs/HttpCommon.js.map +0 -1
  180. package/dist/cjs/HttpShardManager.js +0 -139
  181. package/dist/cjs/HttpShardManager.js.map +0 -1
  182. package/dist/cjs/ShardManager.js +0 -549
  183. package/dist/cjs/ShardManager.js.map +0 -1
  184. package/dist/cjs/ShardStorage.js +0 -151
  185. package/dist/cjs/ShardStorage.js.map +0 -1
  186. package/dist/cjs/SocketShardManager.js +0 -32
  187. package/dist/cjs/SocketShardManager.js.map +0 -1
  188. package/dist/cjs/SqlShardStorage.js +0 -253
  189. package/dist/cjs/SqlShardStorage.js.map +0 -1
  190. package/dist/cjs/SynchronizedClock.js +0 -65
  191. package/dist/cjs/SynchronizedClock.js.map +0 -1
  192. package/dist/cjs/internal/shardManager.js +0 -353
  193. package/dist/cjs/internal/shardManager.js.map +0 -1
  194. package/dist/dts/HttpCommon.d.ts +0 -25
  195. package/dist/dts/HttpCommon.d.ts.map +0 -1
  196. package/dist/dts/HttpShardManager.d.ts +0 -119
  197. package/dist/dts/HttpShardManager.d.ts.map +0 -1
  198. package/dist/dts/ShardManager.d.ts +0 -459
  199. package/dist/dts/ShardManager.d.ts.map +0 -1
  200. package/dist/dts/ShardStorage.d.ts.map +0 -1
  201. package/dist/dts/SocketShardManager.d.ts +0 -17
  202. package/dist/dts/SocketShardManager.d.ts.map +0 -1
  203. package/dist/dts/SqlShardStorage.d.ts +0 -38
  204. package/dist/dts/SqlShardStorage.d.ts.map +0 -1
  205. package/dist/dts/SynchronizedClock.d.ts +0 -19
  206. package/dist/dts/SynchronizedClock.d.ts.map +0 -1
  207. package/dist/dts/internal/shardManager.d.ts +0 -2
  208. package/dist/dts/internal/shardManager.d.ts.map +0 -1
  209. package/dist/esm/HttpCommon.js +0 -38
  210. package/dist/esm/HttpCommon.js.map +0 -1
  211. package/dist/esm/HttpShardManager.js +0 -128
  212. package/dist/esm/HttpShardManager.js.map +0 -1
  213. package/dist/esm/ShardManager.js +0 -535
  214. package/dist/esm/ShardManager.js.map +0 -1
  215. package/dist/esm/ShardStorage.js +0 -141
  216. package/dist/esm/ShardStorage.js.map +0 -1
  217. package/dist/esm/SocketShardManager.js +0 -24
  218. package/dist/esm/SocketShardManager.js.map +0 -1
  219. package/dist/esm/SqlShardStorage.js +0 -244
  220. package/dist/esm/SqlShardStorage.js.map +0 -1
  221. package/dist/esm/SynchronizedClock.js +0 -57
  222. package/dist/esm/SynchronizedClock.js.map +0 -1
  223. package/dist/esm/internal/shardManager.js +0 -342
  224. package/dist/esm/internal/shardManager.js.map +0 -1
  225. package/src/HttpCommon.ts +0 -73
  226. package/src/HttpShardManager.ts +0 -273
  227. package/src/ShardManager.ts +0 -823
  228. package/src/ShardStorage.ts +0 -297
  229. package/src/SocketShardManager.ts +0 -48
  230. package/src/SqlShardStorage.ts +0 -329
  231. package/src/SynchronizedClock.ts +0 -82
  232. package/src/internal/shardManager.ts +0 -412
@@ -1,297 +0,0 @@
1
- /**
2
- * @since 1.0.0
3
- */
4
- import * as Arr from "effect/Array"
5
- import * as Context from "effect/Context"
6
- import * as Effect from "effect/Effect"
7
- import * as Layer from "effect/Layer"
8
- import * as MutableHashMap from "effect/MutableHashMap"
9
- import * as Option from "effect/Option"
10
- import type { PersistenceError } from "./ClusterError.js"
11
- import { Runner } from "./Runner.js"
12
- import { RunnerAddress } from "./RunnerAddress.js"
13
- import { ShardId } from "./ShardId.js"
14
-
15
- /**
16
- * Represents a generic interface to the persistent storage required by the
17
- * cluster.
18
- *
19
- * @since 1.0.0
20
- * @category models
21
- */
22
- export class ShardStorage extends Context.Tag("@effect/cluster/ShardStorage")<ShardStorage, {
23
- /**
24
- * Get the current assignments of shards to runners.
25
- */
26
- readonly getAssignments: Effect.Effect<
27
- Array<[ShardId, Option.Option<RunnerAddress>]>,
28
- PersistenceError
29
- >
30
-
31
- /**
32
- * Save the current state of shards assignments to runners.
33
- */
34
- readonly saveAssignments: (
35
- assignments: Iterable<readonly [ShardId, Option.Option<RunnerAddress>]>
36
- ) => Effect.Effect<void, PersistenceError>
37
-
38
- /**
39
- * Get all runners registered with the cluster.
40
- */
41
- readonly getRunners: Effect.Effect<Array<[RunnerAddress, Runner]>, PersistenceError>
42
-
43
- /**
44
- * Save the current runners registered with the cluster.
45
- */
46
- readonly saveRunners: (runners: Iterable<readonly [RunnerAddress, Runner]>) => Effect.Effect<void, PersistenceError>
47
-
48
- /**
49
- * Try to acquire the given shard ids for processing.
50
- *
51
- * It returns an array of shards it was able to acquire.
52
- */
53
- readonly acquire: (
54
- address: RunnerAddress,
55
- shardIds: Iterable<ShardId>
56
- ) => Effect.Effect<Array<ShardId>, PersistenceError>
57
-
58
- /**
59
- * Refresh the locks owned by the given runner.
60
- *
61
- * Locks expire after 15 seconds, so this method should be called every 10
62
- * seconds to keep the locks alive.
63
- */
64
- readonly refresh: (
65
- address: RunnerAddress,
66
- shardIds: Iterable<ShardId>
67
- ) => Effect.Effect<Array<ShardId>, PersistenceError>
68
-
69
- /**
70
- * Release the given shard ids.
71
- */
72
- readonly release: (
73
- address: RunnerAddress,
74
- shardId: ShardId
75
- ) => Effect.Effect<void, PersistenceError>
76
-
77
- /**
78
- * Release all the shards assigned to the given runner.
79
- */
80
- readonly releaseAll: (address: RunnerAddress) => Effect.Effect<void, PersistenceError>
81
- }>() {}
82
-
83
- /**
84
- * @since 1.0.0
85
- * @category Encoded
86
- */
87
- export interface Encoded {
88
- /**
89
- * Get the current assignments of shards to runners.
90
- */
91
- readonly getAssignments: Effect.Effect<
92
- Array<
93
- readonly [
94
- shardId: string,
95
- runnerAddress: string | null
96
- ]
97
- >,
98
- PersistenceError
99
- >
100
-
101
- /**
102
- * Save the current state of shards assignments to runners.
103
- */
104
- readonly saveAssignments: (
105
- assignments: Array<readonly [shardId: string, RunnerAddress: string | null]>
106
- ) => Effect.Effect<void, PersistenceError>
107
-
108
- /**
109
- * Get all runners registered with the cluster.
110
- */
111
- readonly getRunners: Effect.Effect<Array<readonly [address: string, runner: string]>, PersistenceError>
112
-
113
- /**
114
- * Save the current runners registered with the cluster.
115
- */
116
- readonly saveRunners: (
117
- runners: Array<readonly [address: string, runner: string]>
118
- ) => Effect.Effect<void, PersistenceError>
119
-
120
- /**
121
- * Acquire the lock on the given shards, returning the shards that were
122
- * successfully locked.
123
- */
124
- readonly acquire: (
125
- address: string,
126
- shardIds: ReadonlyArray<string>
127
- ) => Effect.Effect<Array<string>, PersistenceError>
128
-
129
- /**
130
- * Refresh the lock on the given shards, returning the shards that were
131
- * successfully locked.
132
- */
133
- readonly refresh: (
134
- address: string,
135
- shardIds: ReadonlyArray<string>
136
- ) => Effect.Effect<Array<string>, PersistenceError>
137
-
138
- /**
139
- * Release the lock on the given shard.
140
- */
141
- readonly release: (
142
- address: string,
143
- shardId: string
144
- ) => Effect.Effect<void, PersistenceError>
145
-
146
- /**
147
- * Release the lock on all shards for the given runner.
148
- */
149
- readonly releaseAll: (address: string) => Effect.Effect<void, PersistenceError>
150
- }
151
-
152
- /**
153
- * @since 1.0.0
154
- * @category layers
155
- */
156
- export const makeEncoded = (encoded: Encoded) =>
157
- ShardStorage.of({
158
- getAssignments: Effect.map(encoded.getAssignments, (assignments) => {
159
- const arr = Arr.empty<[ShardId, Option.Option<RunnerAddress>]>()
160
- for (const [shardId, runnerAddress] of assignments) {
161
- arr.push([
162
- ShardId.fromString(shardId),
163
- runnerAddress === null ? Option.none() : Option.some(decodeRunnerAddress(runnerAddress))
164
- ])
165
- }
166
- return arr
167
- }),
168
- saveAssignments: (assignments) => {
169
- const arr = Arr.empty<readonly [string, string | null]>()
170
- for (const [shardId, runnerAddress] of assignments) {
171
- arr.push([
172
- shardId.toString(),
173
- Option.isNone(runnerAddress) ? null : encodeRunnerAddress(runnerAddress.value)
174
- ])
175
- }
176
- return encoded.saveAssignments(arr)
177
- },
178
- getRunners: Effect.gen(function*() {
179
- const runners = yield* encoded.getRunners
180
- const results: Array<[RunnerAddress, Runner]> = []
181
- for (let i = 0; i < runners.length; i++) {
182
- const [address, runner] = runners[i]
183
- try {
184
- results.push([decodeRunnerAddress(address), Runner.decodeSync(runner)])
185
- } catch {
186
- //
187
- }
188
- }
189
- return results
190
- }),
191
- saveRunners: (runners) =>
192
- Effect.suspend(() =>
193
- encoded.saveRunners(
194
- Array.from(runners, ([address, runner]) => [encodeRunnerAddress(address), Runner.encodeSync(runner)])
195
- )
196
- ),
197
- acquire: (address, shardIds) => {
198
- const arr = Array.from(shardIds, (id) => id.toString())
199
- return encoded.acquire(encodeRunnerAddress(address), arr).pipe(
200
- Effect.map((shards) => shards.map(ShardId.fromString))
201
- )
202
- },
203
- refresh: (address, shardIds) => {
204
- const arr = Array.from(shardIds, (id) => id.toString())
205
- return encoded.refresh(encodeRunnerAddress(address), arr).pipe(
206
- Effect.map((shards) => shards.map(ShardId.fromString))
207
- )
208
- },
209
- release(address, shardId) {
210
- return encoded.release(encodeRunnerAddress(address), shardId.toString())
211
- },
212
- releaseAll(address) {
213
- return encoded.releaseAll(encodeRunnerAddress(address))
214
- }
215
- })
216
-
217
- /**
218
- * @since 1.0.0
219
- * @category layers
220
- */
221
- export const layerNoop: Layer.Layer<ShardStorage> = Layer.sync(
222
- ShardStorage,
223
- () => {
224
- let acquired: Array<ShardId> = []
225
- return ShardStorage.of({
226
- getAssignments: Effect.sync(() => []),
227
- saveAssignments: () => Effect.void,
228
- getRunners: Effect.sync(() => []),
229
- saveRunners: () => Effect.void,
230
- acquire: (_address, shards) => {
231
- acquired = Array.from(shards)
232
- return Effect.succeed(Array.from(shards))
233
- },
234
- refresh: () => Effect.sync(() => acquired),
235
- release: () => Effect.void,
236
- releaseAll: () => Effect.void
237
- })
238
- }
239
- )
240
-
241
- /**
242
- * @since 1.0.0
243
- * @category constructors
244
- */
245
- export const makeMemory = Effect.gen(function*() {
246
- const assignments = MutableHashMap.empty<ShardId, Option.Option<RunnerAddress>>()
247
- const runners = MutableHashMap.empty<RunnerAddress, Runner>()
248
-
249
- function saveAssignments(value: Iterable<readonly [ShardId, Option.Option<RunnerAddress>]>) {
250
- return Effect.sync(() => {
251
- for (const [shardId, runnerAddress] of value) {
252
- MutableHashMap.set(assignments, shardId, runnerAddress)
253
- }
254
- })
255
- }
256
-
257
- function saveRunners(value: Iterable<readonly [RunnerAddress, Runner]>) {
258
- return Effect.sync(() => {
259
- for (const [address, runner] of value) {
260
- MutableHashMap.set(runners, address, runner)
261
- }
262
- })
263
- }
264
-
265
- let acquired: Array<ShardId> = []
266
-
267
- return ShardStorage.of({
268
- getAssignments: Effect.sync(() => Array.from(assignments)),
269
- saveAssignments,
270
- getRunners: Effect.sync(() => Array.from(runners)),
271
- saveRunners,
272
- acquire: (_address, shardIds) => {
273
- acquired = Array.from(shardIds)
274
- return Effect.succeed(Array.from(shardIds))
275
- },
276
- refresh: () => Effect.sync(() => acquired),
277
- release: () => Effect.void,
278
- releaseAll: () => Effect.void
279
- })
280
- })
281
-
282
- /**
283
- * @since 1.0.0
284
- * @category layers
285
- */
286
- export const layerMemory: Layer.Layer<ShardStorage> = Layer.effect(ShardStorage, makeMemory)
287
-
288
- // -------------------------------------------------------------------------------------
289
- // internal
290
- // -------------------------------------------------------------------------------------
291
-
292
- const encodeRunnerAddress = (runnerAddress: RunnerAddress) => `${runnerAddress.host}:${runnerAddress.port}`
293
-
294
- const decodeRunnerAddress = (runnerAddress: string): RunnerAddress => {
295
- const [host, port] = runnerAddress.split(":")
296
- return new RunnerAddress({ host, port: Number(port) })
297
- }
@@ -1,48 +0,0 @@
1
- /**
2
- * @since 1.0.0
3
- */
4
- import { SocketServer } from "@effect/platform/SocketServer"
5
- import type { RpcSerialization } from "@effect/rpc/RpcSerialization"
6
- import * as RpcServer from "@effect/rpc/RpcServer"
7
- import * as Effect from "effect/Effect"
8
- import * as Layer from "effect/Layer"
9
- import * as MessageStorage from "./MessageStorage.js"
10
- import type { RunnerHealth } from "./RunnerHealth.js"
11
- import * as Runners from "./Runners.js"
12
- import type { ShardingConfig } from "./ShardingConfig.js"
13
- import * as ShardManager from "./ShardManager.js"
14
- import type { ShardStorage } from "./ShardStorage.js"
15
-
16
- const withLogAddress = <A, E, R>(layer: Layer.Layer<A, E, R>): Layer.Layer<A, E, R | SocketServer> =>
17
- Layer.effectDiscard(Effect.gen(function*() {
18
- const server = yield* SocketServer
19
- const address = server.address._tag === "UnixAddress"
20
- ? server.address.path
21
- : `${server.address.hostname}:${server.address.port}`
22
- yield* Effect.annotateLogs(Effect.logInfo(`Listening on: ${address}`), {
23
- package: "@effect/cluster",
24
- service: "ShardManager"
25
- })
26
- })).pipe(Layer.provideMerge(layer))
27
-
28
- /**
29
- * @since 1.0.0
30
- * @category Layers
31
- */
32
- export const layer: Layer.Layer<
33
- ShardManager.ShardManager,
34
- never,
35
- | ShardStorage
36
- | SocketServer
37
- | Runners.RpcClientProtocol
38
- | RpcSerialization
39
- | RunnerHealth
40
- | ShardManager.Config
41
- | ShardingConfig
42
- > = ShardManager.layerServer.pipe(
43
- withLogAddress,
44
- Layer.provide(Layer.fresh(RpcServer.layerProtocolSocketServer)),
45
- Layer.provideMerge(ShardManager.layer),
46
- Layer.provide(Runners.layerRpc),
47
- Layer.provide(MessageStorage.layerNoop)
48
- )
@@ -1,329 +0,0 @@
1
- /**
2
- * @since 1.0.0
3
- */
4
- import * as SqlClient from "@effect/sql/SqlClient"
5
- import type { SqlError } from "@effect/sql/SqlError"
6
- import * as Arr from "effect/Array"
7
- import * as Effect from "effect/Effect"
8
- import * as Layer from "effect/Layer"
9
- import { PersistenceError } from "./ClusterError.js"
10
- import * as ShardStorage from "./ShardStorage.js"
11
-
12
- const withTracerDisabled = Effect.withTracerEnabled(false)
13
-
14
- /**
15
- * @since 1.0.0
16
- * @category Constructors
17
- */
18
- export const make = Effect.fnUntraced(function*(options?: {
19
- readonly prefix?: string | undefined
20
- }) {
21
- const sql = (yield* SqlClient.SqlClient).withoutTransforms()
22
- const prefix = options?.prefix ?? "cluster"
23
- const table = (name: string) => `${prefix}_${name}`
24
-
25
- const runnersTable = table("runners")
26
- const runnersTableSql = sql(runnersTable)
27
-
28
- yield* sql.onDialectOrElse({
29
- mssql: () =>
30
- sql`
31
- IF OBJECT_ID(N'${runnersTableSql}', N'U') IS NULL
32
- CREATE TABLE ${runnersTableSql} (
33
- address VARCHAR(255) PRIMARY KEY,
34
- runner TEXT NOT NULL
35
- )
36
- `,
37
- mysql: () =>
38
- sql`
39
- CREATE TABLE IF NOT EXISTS ${runnersTableSql} (
40
- address VARCHAR(255) PRIMARY KEY,
41
- runner TEXT NOT NULL
42
- )
43
- `,
44
- pg: () =>
45
- sql`
46
- CREATE TABLE IF NOT EXISTS ${runnersTableSql} (
47
- address VARCHAR(255) PRIMARY KEY,
48
- runner TEXT NOT NULL
49
- )
50
- `,
51
- orElse: () =>
52
- // sqlite
53
- sql`
54
- CREATE TABLE IF NOT EXISTS ${runnersTableSql} (
55
- address TEXT PRIMARY KEY,
56
- runner TEXT NOT NULL
57
- )
58
- `
59
- })
60
-
61
- const shardsTable = table("shards")
62
- const shardsTableSql = sql(shardsTable)
63
-
64
- yield* sql.onDialectOrElse({
65
- mssql: () =>
66
- sql`
67
- IF OBJECT_ID(N'${shardsTableSql}', N'U') IS NULL
68
- CREATE TABLE ${shardsTableSql} (
69
- shard_id VARCHAR(50) PRIMARY KEY,
70
- address VARCHAR(255)
71
- )
72
- `,
73
- mysql: () =>
74
- sql`
75
- CREATE TABLE IF NOT EXISTS ${shardsTableSql} (
76
- shard_id VARCHAR(50) PRIMARY KEY,
77
- address VARCHAR(255)
78
- )
79
- `,
80
- pg: () =>
81
- sql`
82
- CREATE TABLE IF NOT EXISTS ${shardsTableSql} (
83
- shard_id VARCHAR(50) PRIMARY KEY,
84
- address VARCHAR(255)
85
- )
86
- `,
87
- orElse: () =>
88
- // sqlite
89
- sql`
90
- CREATE TABLE IF NOT EXISTS ${shardsTableSql} (
91
- shard_id TEXT PRIMARY KEY,
92
- address TEXT
93
- )
94
- `
95
- })
96
-
97
- const locksTable = table("locks")
98
- const locksTableSql = sql(locksTable)
99
-
100
- yield* sql.onDialectOrElse({
101
- mssql: () =>
102
- sql`
103
- IF OBJECT_ID(N'${locksTableSql}', N'U') IS NULL
104
- CREATE TABLE ${locksTableSql} (
105
- shard_id VARCHAR(50) PRIMARY KEY,
106
- address VARCHAR(255) NOT NULL,
107
- acquired_at DATETIME NOT NULL
108
- )
109
- `,
110
- mysql: () =>
111
- sql`
112
- CREATE TABLE IF NOT EXISTS ${locksTableSql} (
113
- shard_id VARCHAR(50) PRIMARY KEY,
114
- address VARCHAR(255) NOT NULL,
115
- acquired_at DATETIME NOT NULL
116
- )
117
- `,
118
- pg: () =>
119
- sql`
120
- CREATE TABLE IF NOT EXISTS ${locksTableSql} (
121
- shard_id VARCHAR(50) PRIMARY KEY,
122
- address VARCHAR(255) NOT NULL,
123
- acquired_at TIMESTAMP NOT NULL
124
- )
125
- `,
126
- orElse: () =>
127
- // sqlite
128
- sql`
129
- CREATE TABLE IF NOT EXISTS ${locksTableSql} (
130
- shard_id TEXT PRIMARY KEY,
131
- address TEXT NOT NULL,
132
- acquired_at DATETIME NOT NULL
133
- )
134
- `
135
- })
136
-
137
- const sqlNowString = sql.onDialectOrElse({
138
- pg: () => "NOW()",
139
- mysql: () => "NOW()",
140
- mssql: () => "GETDATE()",
141
- orElse: () => "CURRENT_TIMESTAMP"
142
- })
143
- const sqlNow = sql.literal(sqlNowString)
144
-
145
- const lockExpiresAt = sql.onDialectOrElse({
146
- pg: () => sql`${sqlNow} - INTERVAL '5 seconds'`,
147
- mysql: () => sql`DATE_SUB(${sqlNow}, INTERVAL 5 SECOND)`,
148
- mssql: () => sql`DATEADD(SECOND, -5, ${sqlNow})`,
149
- orElse: () => sql`datetime(${sqlNow}, '-5 seconds')`
150
- })
151
-
152
- const acquireLock = sql.onDialectOrElse({
153
- pg: () => (address: string, values: Array<any>) =>
154
- sql`
155
- INSERT INTO ${locksTableSql} (shard_id, address, acquired_at) VALUES ${sql.csv(values)}
156
- ON CONFLICT (shard_id) DO UPDATE
157
- SET address = ${address}, acquired_at = ${sqlNow}
158
- WHERE ${locksTableSql}.address = ${address}
159
- OR ${locksTableSql}.acquired_at < ${lockExpiresAt}
160
- `,
161
- mysql: () => (_address: string, values: Array<any>) =>
162
- sql`
163
- INSERT INTO ${locksTableSql} (shard_id, address, acquired_at) VALUES ${sql.csv(values)}
164
- ON DUPLICATE KEY UPDATE
165
- address = IF(address = VALUES(address) OR acquired_at < ${lockExpiresAt}, VALUES(address), address),
166
- acquired_at = IF(address = VALUES(address) OR acquired_at < ${lockExpiresAt}, VALUES(acquired_at), acquired_at)
167
- `.unprepared,
168
- mssql: () => (_address: string, values: Array<any>) =>
169
- sql`
170
- MERGE ${locksTableSql} WITH (HOLDLOCK) AS target
171
- USING (SELECT * FROM (VALUES ${sql.csv(values)})) AS source (shard_id, address, acquired_at)
172
- ON target.shard_id = source.shard_id
173
- WHEN MATCHED AND (target.address = source.address OR DATEDIFF(SECOND, target.acquired_at, ${sqlNow}) > 5) THEN
174
- UPDATE SET address = source.address, acquired_at = source.acquired_at
175
- WHEN NOT MATCHED THEN
176
- INSERT (shard_id, address, acquired_at)
177
- VALUES (source.shard_id, source.address, source.acquired_at);
178
- `,
179
- orElse: () => (address: string, values: Array<any>) =>
180
- // sqlite
181
- sql`
182
- WITH source(shard_id, address, acquired_at) AS (VALUES ${sql.csv(values)})
183
- INSERT INTO ${locksTableSql} (shard_id, address, acquired_at)
184
- SELECT source.shard_id, source.address, source.acquired_at
185
- FROM source
186
- WHERE NOT EXISTS (
187
- SELECT 1 FROM ${locksTableSql}
188
- WHERE shard_id = source.shard_id
189
- AND address != ${address}
190
- AND (strftime('%s', ${sqlNow}) - strftime('%s', acquired_at)) <= 5
191
- )
192
- ON CONFLICT(shard_id) DO UPDATE
193
- SET address = ${address}, acquired_at = ${sqlNow}
194
- `
195
- })
196
-
197
- const wrapString = sql.onDialectOrElse({
198
- mssql: () => (s: string) => `N'${s}'`,
199
- orElse: () => (s: string) => `'${s}'`
200
- })
201
- const wrapStringArr = (arr: ReadonlyArray<string>) => sql.literal(arr.map(wrapString).join(", "))
202
-
203
- const refreshShards = sql.onDialectOrElse({
204
- mysql: () => (address: string, shardIds: ReadonlyArray<string>) => {
205
- const shardIdsStr = wrapStringArr(shardIds)
206
- return sql<Array<{ shard_id: string }>>`
207
- UPDATE ${locksTableSql}
208
- SET acquired_at = ${sqlNow}
209
- WHERE address = ${address} AND shard_id IN (${shardIdsStr});
210
- SELECT shard_id FROM ${locksTableSql} WHERE address = ${address} AND shard_id IN (${shardIdsStr})
211
- `.unprepared.pipe(
212
- Effect.map((rows) => rows[1].map((row) => [row.shard_id]))
213
- )
214
- },
215
- mssql: () => (address: string, shardIds: ReadonlyArray<string>) =>
216
- sql`
217
- UPDATE ${locksTableSql}
218
- SET acquired_at = ${sqlNow}
219
- OUTPUT inserted.shard_id
220
- WHERE address = ${address} AND shard_id IN (${wrapStringArr(shardIds)})
221
- `.values,
222
- orElse: () => (address: string, shardIds: ReadonlyArray<string>) =>
223
- sql`
224
- UPDATE ${locksTableSql}
225
- SET acquired_at = ${sqlNow}
226
- WHERE address = ${address} AND shard_id IN (${wrapStringArr(shardIds)})
227
- RETURNING shard_id
228
- `.values
229
- })
230
-
231
- return ShardStorage.makeEncoded({
232
- getAssignments: sql`SELECT shard_id, address FROM ${shardsTableSql} ORDER BY shard_id`.values.pipe(
233
- PersistenceError.refail,
234
- withTracerDisabled
235
- ) as any,
236
-
237
- saveAssignments: (assignments) => {
238
- const remove = sql`DELETE FROM ${shardsTableSql}`
239
- if (assignments.length === 0) {
240
- return PersistenceError.refail(remove)
241
- }
242
- const values = assignments.map(([shardId, address]) => sql`(${shardId}, ${address})`)
243
- return remove.pipe(
244
- Effect.andThen(sql`INSERT INTO ${shardsTableSql} (shard_id, address) VALUES ${sql.csv(values)}`.unprepared),
245
- sql.withTransaction,
246
- PersistenceError.refail,
247
- withTracerDisabled
248
- )
249
- },
250
-
251
- getRunners: sql`SELECT address, runner FROM ${runnersTableSql}`.values.pipe(
252
- PersistenceError.refail,
253
- Effect.map(Arr.map(([address, runner]) => [String(address), String(runner)] as const)),
254
- withTracerDisabled
255
- ),
256
-
257
- saveRunners: (runners) => {
258
- const remove = sql`DELETE FROM ${runnersTableSql}`
259
- if (runners.length === 0) {
260
- return PersistenceError.refail(remove)
261
- }
262
- const values = runners.map(([address, runner]) => sql`(${address}, ${runner})`)
263
- const insert = sql`INSERT INTO ${runnersTableSql} (address, runner) VALUES ${sql.csv(values)}`.unprepared
264
- return remove.pipe(
265
- Effect.andThen(insert),
266
- sql.withTransaction,
267
- PersistenceError.refail,
268
- withTracerDisabled
269
- )
270
- },
271
-
272
- acquire: Effect.fnUntraced(
273
- function*(address, shardIds) {
274
- if (shardIds.length > 0) {
275
- const values = shardIds.map((shardId) => sql`(${shardId}, ${address}, ${sqlNow})`)
276
- yield* acquireLock(address, values)
277
- }
278
- const currentLocks = yield* sql<{ shard_id: string }>`
279
- SELECT shard_id FROM ${sql(locksTable)}
280
- WHERE address = ${address} AND acquired_at >= ${lockExpiresAt}
281
- `.values
282
- return currentLocks.map((row) => row[0] as string)
283
- },
284
- sql.withTransaction,
285
- PersistenceError.refail,
286
- withTracerDisabled
287
- ),
288
-
289
- refresh: (address, shardIds) =>
290
- shardIds.length === 0
291
- ? Effect.succeed([])
292
- : refreshShards(address, shardIds).pipe(
293
- Effect.map((rows) => rows.map((row) => row[0] as string)),
294
- PersistenceError.refail,
295
- withTracerDisabled
296
- ),
297
-
298
- release: (address, shardId) =>
299
- sql`DELETE FROM ${locksTableSql} WHERE address = ${address} AND shard_id = ${shardId}`.pipe(
300
- PersistenceError.refail,
301
- withTracerDisabled
302
- ),
303
-
304
- releaseAll: (address) =>
305
- sql`DELETE FROM ${locksTableSql} WHERE address = ${address}`.pipe(
306
- PersistenceError.refail,
307
- withTracerDisabled
308
- )
309
- })
310
- }, withTracerDisabled)
311
-
312
- /**
313
- * @since 1.0.0
314
- * @category Layers
315
- */
316
- export const layer: Layer.Layer<
317
- ShardStorage.ShardStorage,
318
- SqlError,
319
- SqlClient.SqlClient
320
- > = Layer.effect(ShardStorage.ShardStorage, make())
321
-
322
- /**
323
- * @since 1.0.0
324
- * @category Layers
325
- */
326
- export const layerWith = (options: {
327
- readonly prefix?: string | undefined
328
- }): Layer.Layer<ShardStorage.ShardStorage, SqlError, SqlClient.SqlClient> =>
329
- Layer.scoped(ShardStorage.ShardStorage, make(options))