@hotmeshio/hotmesh 0.1.15 → 0.1.17
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/README.md +623 -209
- package/build/index.d.ts +14 -3
- package/build/index.js +17 -4
- package/build/modules/enums.d.ts +12 -12
- package/build/modules/enums.js +15 -25
- package/build/modules/errors.d.ts +16 -16
- package/build/modules/errors.js +28 -28
- package/build/modules/key.d.ts +0 -37
- package/build/modules/key.js +4 -45
- package/build/modules/utils.d.ts +7 -15
- package/build/modules/utils.js +21 -44
- package/build/package.json +18 -15
- package/build/services/activities/activity.d.ts +0 -31
- package/build/services/activities/activity.js +1 -50
- package/build/services/activities/await.js +0 -4
- package/build/services/activities/cycle.d.ts +0 -7
- package/build/services/activities/cycle.js +1 -16
- package/build/services/activities/hook.d.ts +0 -6
- package/build/services/activities/hook.js +2 -12
- package/build/services/activities/interrupt.js +0 -8
- package/build/services/activities/signal.d.ts +0 -6
- package/build/services/activities/signal.js +0 -15
- package/build/services/activities/trigger.d.ts +4 -5
- package/build/services/activities/trigger.js +22 -16
- package/build/services/activities/worker.js +0 -4
- package/build/services/collator/index.d.ts +0 -70
- package/build/services/collator/index.js +1 -91
- package/build/services/compiler/deployer.js +6 -38
- package/build/services/compiler/index.d.ts +0 -15
- package/build/services/compiler/index.js +0 -20
- package/build/services/compiler/validator.d.ts +0 -3
- package/build/services/compiler/validator.js +0 -25
- package/build/services/connector/clients/ioredis.js +0 -2
- package/build/services/connector/clients/redis.js +0 -2
- package/build/services/connector/index.js +0 -2
- package/build/services/engine/index.d.ts +1 -10
- package/build/services/engine/index.js +1 -48
- package/build/services/exporter/index.d.ts +0 -27
- package/build/services/exporter/index.js +0 -33
- package/build/services/hotmesh/index.d.ts +8 -4
- package/build/services/hotmesh/index.js +20 -19
- package/build/services/logger/index.js +0 -2
- package/build/services/mapper/index.d.ts +0 -14
- package/build/services/mapper/index.js +0 -14
- package/build/services/meshcall/index.d.ts +21 -0
- package/build/services/meshcall/index.js +202 -0
- package/build/services/meshcall/schemas/factory.d.ts +2 -0
- package/build/services/meshcall/schemas/factory.js +179 -0
- package/build/services/meshdata/index.d.ts +75 -0
- package/build/services/meshdata/index.js +541 -0
- package/build/services/meshflow/client.d.ts +18 -0
- package/build/services/{durable → meshflow}/client.js +9 -40
- package/build/services/{durable → meshflow}/connection.d.ts +2 -1
- package/build/services/{durable → meshflow}/connection.js +1 -0
- package/build/services/meshflow/exporter.d.ts +29 -0
- package/build/services/{durable → meshflow}/exporter.js +0 -29
- package/build/services/meshflow/handle.d.ts +22 -0
- package/build/services/{durable → meshflow}/handle.js +0 -46
- package/build/services/meshflow/index.d.ts +17 -0
- package/build/services/meshflow/index.js +23 -0
- package/build/services/meshflow/schemas/factory.d.ts +4 -0
- package/build/services/{durable → meshflow}/schemas/factory.js +2 -30
- package/build/services/meshflow/search.d.ts +23 -0
- package/build/services/{durable → meshflow}/search.js +0 -99
- package/build/services/{durable → meshflow}/worker.d.ts +3 -2
- package/build/services/{durable → meshflow}/worker.js +23 -39
- package/build/services/meshflow/workflow.d.ts +27 -0
- package/build/services/{durable → meshflow}/workflow.js +27 -169
- package/build/services/pipe/functions/date.d.ts +0 -7
- package/build/services/pipe/functions/date.js +0 -7
- package/build/services/pipe/functions/math.js +0 -2
- package/build/services/pipe/index.d.ts +0 -15
- package/build/services/pipe/index.js +2 -23
- package/build/services/quorum/index.d.ts +1 -7
- package/build/services/quorum/index.js +0 -21
- package/build/services/reporter/index.d.ts +0 -5
- package/build/services/reporter/index.js +0 -9
- package/build/services/router/index.d.ts +0 -9
- package/build/services/router/index.js +2 -30
- package/build/services/serializer/index.js +6 -23
- package/build/services/store/cache.d.ts +0 -19
- package/build/services/store/cache.js +0 -19
- package/build/services/store/clients/ioredis.d.ts +0 -6
- package/build/services/store/clients/ioredis.js +0 -7
- package/build/services/store/clients/redis.d.ts +0 -6
- package/build/services/store/clients/redis.js +0 -6
- package/build/services/store/index.d.ts +0 -55
- package/build/services/store/index.js +14 -87
- package/build/services/stream/clients/ioredis.js +1 -4
- package/build/services/task/index.d.ts +0 -9
- package/build/services/task/index.js +0 -31
- package/build/services/telemetry/index.d.ts +0 -7
- package/build/services/telemetry/index.js +1 -13
- package/build/services/worker/index.d.ts +1 -4
- package/build/services/worker/index.js +0 -6
- package/build/types/activity.d.ts +0 -81
- package/build/types/error.d.ts +5 -5
- package/build/types/exporter.d.ts +1 -14
- package/build/types/hotmesh.d.ts +4 -12
- package/build/types/hotmesh.js +0 -3
- package/build/types/index.d.ts +5 -3
- package/build/types/index.js +1 -1
- package/build/types/job.d.ts +1 -95
- package/build/types/meshcall.d.ts +54 -0
- package/build/types/meshdata.d.ts +59 -0
- package/build/types/meshdata.js +2 -0
- package/build/types/meshflow.d.ts +202 -0
- package/build/types/meshflow.js +2 -0
- package/build/types/pipe.d.ts +0 -65
- package/build/types/quorum.d.ts +0 -12
- package/build/types/redis.d.ts +0 -6
- package/build/types/stream.d.ts +0 -59
- package/build/types/stream.js +0 -4
- package/index.ts +12 -3
- package/package.json +18 -15
- package/typedoc.json +38 -0
- package/types/error.ts +5 -5
- package/types/exporter.ts +1 -1
- package/types/hotmesh.ts +3 -2
- package/types/index.ts +25 -7
- package/types/job.ts +19 -1
- package/types/meshcall.ts +192 -0
- package/types/meshdata.ts +273 -0
- package/types/{durable.ts → meshflow.ts} +33 -9
- package/build/services/durable/client.d.ts +0 -49
- package/build/services/durable/exporter.d.ts +0 -51
- package/build/services/durable/handle.d.ts +0 -58
- package/build/services/durable/index.d.ts +0 -19
- package/build/services/durable/index.js +0 -25
- package/build/services/durable/schemas/factory.d.ts +0 -33
- package/build/services/durable/search.d.ts +0 -120
- package/build/services/durable/workflow.d.ts +0 -143
- package/build/types/durable.d.ts +0 -467
- /package/build/types/{durable.js → meshcall.js} +0 -0
package/README.md
CHANGED
|
@@ -1,37 +1,187 @@
|
|
|
1
1
|
# HotMesh
|
|
2
2
|

|
|
3
3
|
|
|
4
|
-
HotMesh transforms Redis into
|
|
5
|
-
|
|
6
|
-
*Write functions in your own style, and let Redis govern their execution, reliably and durably.*
|
|
4
|
+
**HotMesh** transforms **Redis** into indispensable **Middleware**. Connect **anything** to **everything**.
|
|
7
5
|
|
|
8
6
|
## Install
|
|
9
|
-
[](https://badge.fury.io/js/%40hotmeshio%2Fhotmesh)
|
|
10
|
-
|
|
11
7
|
```sh
|
|
12
8
|
npm install @hotmeshio/hotmesh
|
|
13
9
|
```
|
|
10
|
+
You have a Redis instance? Good. You're ready to go.
|
|
11
|
+
|
|
12
|
+
## SDK Docs
|
|
13
|
+
[Read the Docs](https://hotmeshio.github.io/sdk-typescript/)
|
|
14
|
+
|
|
15
|
+
## MeshCall | Connect Everything
|
|
16
|
+
The **MeshCall** module connects any function with a connection to Redis. Function responses are cacheable and functions can even run as cyclical cron jobs. Make blazing fast interservice calls that return in milliseconds without the overhead of HTTP.
|
|
17
|
+
|
|
18
|
+
<details style="padding: .5em">
|
|
19
|
+
<summary style="font-size:1.25em;">Run an idempotent cron job</summary>
|
|
20
|
+
|
|
21
|
+
### Run a Cron
|
|
22
|
+
This example demonstrates an *idempotent* cron that runs every day. The `id` makes each cron job unique and ensures that only one instance runs, despite repeated invocations. *The `cron` method fails silently if a workflow is already running with the same `id`.*
|
|
23
|
+
|
|
24
|
+
Optionally set a `delay` and/or set `maxCycles` to limit the number of cycles.
|
|
25
|
+
|
|
26
|
+
1. Define the cron function.
|
|
27
|
+
```typescript
|
|
28
|
+
//cron.ts
|
|
29
|
+
import { MeshCall } from '@hotmeshio/hotmesh';
|
|
30
|
+
import * as Redis from 'redis';
|
|
31
|
+
|
|
32
|
+
export const runMyCron = (id: string, interval = '1 day') => {
|
|
33
|
+
MeshCall.cron({
|
|
34
|
+
topic: 'my.cron.function',
|
|
35
|
+
redis: {
|
|
36
|
+
class: Redis,
|
|
37
|
+
options: { url: 'redis://:key_admin@redis:6379' }
|
|
38
|
+
},
|
|
39
|
+
callback: async () => {
|
|
40
|
+
//your code here...
|
|
41
|
+
},
|
|
42
|
+
options: { id, interval, maxCycles: 24 }
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
2. Call `runMyCron` at server startup (or call as needed to run multiple crons).
|
|
48
|
+
```typescript
|
|
49
|
+
//server.ts
|
|
50
|
+
import { runMyCron } from './cron';
|
|
51
|
+
runMyCron('myDailyCron123');
|
|
52
|
+
```
|
|
53
|
+
</details>
|
|
54
|
+
|
|
55
|
+
<details style="padding: .5em">
|
|
56
|
+
<summary style="font-size:1.25em;">Interrupt a cron job</summary>
|
|
57
|
+
|
|
58
|
+
### Interrupt a Cron
|
|
59
|
+
This example demonstrates how to cancel a running cron job.
|
|
60
|
+
|
|
61
|
+
1. Use the same `id` and `topic` that were used to create the cron to cancel it.
|
|
62
|
+
```typescript
|
|
63
|
+
import { MeshCall } from '@hotmeshio/hotmesh';
|
|
64
|
+
import * as Redis from 'redis';
|
|
65
|
+
|
|
66
|
+
MeshCall.interrupt({
|
|
67
|
+
topic: 'my.cron.function',
|
|
68
|
+
redis: {
|
|
69
|
+
class: Redis,
|
|
70
|
+
options: { url: 'redis://:key_admin@redis:6379' }
|
|
71
|
+
},
|
|
72
|
+
options: { id: 'myDailyCron123' }
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
</details>
|
|
76
|
+
|
|
77
|
+
<details style="padding: .5em">
|
|
78
|
+
<summary style="font-size:1.25em;">Call any function in any service</summary>
|
|
79
|
+
|
|
80
|
+
### Call a Function
|
|
81
|
+
Make blazing fast interservice calls that behave like HTTP but without the setup and performance overhead. This example demonstrates how to connect a function to the mesh and call it from anywhere on the network.
|
|
82
|
+
|
|
83
|
+
1. Call `MeshCall.connect` and provide a `topic` to uniquely identify the function.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
//myFunctionWrapper.ts
|
|
87
|
+
import { MeshCall, Types } from '@hotmeshio/hotmesh';
|
|
88
|
+
import * as Redis from 'redis';
|
|
14
89
|
|
|
15
|
-
|
|
16
|
-
|
|
90
|
+
export const connectMyFunction = () => {
|
|
91
|
+
MeshCall.connect({
|
|
92
|
+
topic: 'my.demo.function',
|
|
93
|
+
redis: {
|
|
94
|
+
class: Redis,
|
|
95
|
+
options: { url: 'redis://:key_admin@redis:6379' }
|
|
96
|
+
},
|
|
97
|
+
callback: async (input: string) => {
|
|
98
|
+
//your code goes here; response must be JSON serializable
|
|
99
|
+
return { hello: input }
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
2. Call `connectMyFunction` at server startup to connect your function to the mesh.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
//server.ts
|
|
109
|
+
import { connectMyFunction } from './myFunctionWrapper';
|
|
110
|
+
connectMyFunction();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
3. Call your function from anywhere on the network (or even from the same service). Send any payload as long as it's JSON serializable.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { MeshCall } from '@hotmeshio/hotmesh';
|
|
117
|
+
import * as Redis from 'redis';
|
|
118
|
+
|
|
119
|
+
const result = await MeshCall.exec({
|
|
120
|
+
topic: 'my.demo.function',
|
|
121
|
+
args: ['something'],
|
|
122
|
+
redis: {
|
|
123
|
+
class: Redis,
|
|
124
|
+
options: { url: 'redis://:key_admin@redis:6379' }
|
|
125
|
+
},
|
|
126
|
+
}); //returns `{ hello: 'something'}`
|
|
127
|
+
```
|
|
128
|
+
</details>
|
|
17
129
|
|
|
18
|
-
<
|
|
130
|
+
<details style="padding: .5em">
|
|
131
|
+
<summary style="font-size:1.25em;">Call and <b>cache</b> a function</summary>
|
|
132
|
+
|
|
133
|
+
### Cache a Function
|
|
134
|
+
Redis is great for unburdening stressed services. This solution builds upon the previous example, caching the response. The linked function will only be re/called when the cached result expires. Everything remains the same, except the caller which specifies an `id` and `ttl`.
|
|
135
|
+
|
|
136
|
+
1. Make the call from another service (or even the same service). Include an `id` and `ttl` to cache the result for the specified duration.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { MeshCall } from '@hotmeshio/hotmesh';
|
|
140
|
+
import * as Redis from 'redis';
|
|
141
|
+
|
|
142
|
+
const result = await MeshCall.exec({
|
|
143
|
+
topic: 'my.demo.function',
|
|
144
|
+
args: ['anything'],
|
|
145
|
+
redis: {
|
|
146
|
+
class: Redis,
|
|
147
|
+
options: { url: 'redis://:key_admin@redis:6379' }
|
|
148
|
+
},
|
|
149
|
+
options: { id: 'myid123', ttl: '15 minutes' },
|
|
150
|
+
}); //returns `{ hello: 'anything'}`
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
2. Flush the cache at any time, using the same `topic` and cache `id`.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { MeshCall } from '@hotmeshio/hotmesh';
|
|
157
|
+
import * as Redis from 'redis';
|
|
158
|
+
|
|
159
|
+
await MeshCall.flush({
|
|
160
|
+
topic: 'my.demo.function',
|
|
161
|
+
redis: {
|
|
162
|
+
class: Redis,
|
|
163
|
+
options: { url: 'redis://:key_admin@redis:6379' }
|
|
164
|
+
},
|
|
165
|
+
options: { id: 'myid123' },
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
</details>
|
|
19
169
|
|
|
20
|
-
|
|
170
|
+
## MeshFlow | Transactional Workflow
|
|
171
|
+
The **MeshFlow** module is a drop-in replacement for [Temporal.io](https://temporal.io). If you need to orchestrate your functions as durable workflows, MeshFlow combines the popular Temporal SDK with Redis' *in-memory execution speed*.
|
|
21
172
|
|
|
22
|
-
|
|
23
|
-
|
|
173
|
+
<details style="padding: .5em">
|
|
174
|
+
<summary style="font-size:1.25em;">Orchestrate unpredictable activities</summary>
|
|
24
175
|
|
|
25
|
-
###
|
|
26
|
-
|
|
176
|
+
### Proxy Activities
|
|
177
|
+
When an endpoint is unpredictable, use `proxyActivities`. HotMesh will retry as necessary until the call succeeds. This example demonstrates a workflow that greets a user in both English and Spanish. Even though both activities throw random errors, the workflow always returns a successful result.
|
|
27
178
|
|
|
28
|
-
|
|
29
|
-
HotMesh's *Durable* module is a TypeScript Library modeled after Temporal.io. If you're familiar with their SDK, the principles are the same.
|
|
179
|
+
1. Start by defining **activities**. Note how each throws an error 50% of the time.
|
|
30
180
|
|
|
31
|
-
|
|
32
|
-
```javascript
|
|
181
|
+
```typescript
|
|
33
182
|
//activities.ts
|
|
34
183
|
export async function greet(name: string): Promise<string> {
|
|
184
|
+
if (Math.random() > 0.5) throw new Error('Random error');
|
|
35
185
|
return `Hello, ${name}!`;
|
|
36
186
|
}
|
|
37
187
|
|
|
@@ -40,62 +190,67 @@ HotMesh's *Durable* module is a TypeScript Library modeled after Temporal.io. If
|
|
|
40
190
|
return `¡Hola, ${nombre}!`;
|
|
41
191
|
}
|
|
42
192
|
```
|
|
43
|
-
|
|
44
|
-
|
|
193
|
+
|
|
194
|
+
2. Define the **workflow** logic. Include conditional branching, loops, etc to control activity execution. It's vanilla JavaScript written in your own coding style. The only requirement is to use `proxyActivities`, ensuring your activities are executed with HotMesh's durability wrapper.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
45
197
|
//workflows.ts
|
|
46
|
-
import {
|
|
198
|
+
import { MeshFlow } from '@hotmeshio/hotmesh';
|
|
47
199
|
import * as activities from './activities';
|
|
48
200
|
|
|
49
|
-
const { greet, saludar } =
|
|
201
|
+
const { greet, saludar } = MeshFlow.workflow
|
|
50
202
|
.proxyActivities<typeof activities>({
|
|
51
203
|
activities
|
|
52
204
|
});
|
|
53
205
|
|
|
54
|
-
export async function example(name: string
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
206
|
+
export async function example(name: string): Promise<[string, string]> {
|
|
207
|
+
return Promise.all([
|
|
208
|
+
greet(name),
|
|
209
|
+
saludar(name)
|
|
210
|
+
]);
|
|
60
211
|
}
|
|
61
212
|
```
|
|
213
|
+
|
|
62
214
|
3. Instance a HotMesh **client** to invoke the workflow.
|
|
63
|
-
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
64
217
|
//client.ts
|
|
65
|
-
import {
|
|
66
|
-
import Redis from 'ioredis';
|
|
218
|
+
import { MeshFlow, HotMesh } from '@hotmeshio/hotmesh';
|
|
219
|
+
import Redis from 'ioredis';
|
|
67
220
|
|
|
68
221
|
async function run(): Promise<string> {
|
|
69
|
-
const client = new
|
|
222
|
+
const client = new MeshFlow.Client({
|
|
70
223
|
connection: {
|
|
71
224
|
class: Redis,
|
|
72
|
-
options: { host: '
|
|
225
|
+
options: { host: 'redis', port: 6379 }
|
|
73
226
|
}
|
|
74
227
|
});
|
|
75
228
|
|
|
76
|
-
const handle = await client.workflow.start({
|
|
77
|
-
args: ['HotMesh'
|
|
229
|
+
const handle = await client.workflow.start<[string,string]>({
|
|
230
|
+
args: ['HotMesh'],
|
|
78
231
|
taskQueue: 'default',
|
|
79
232
|
workflowName: 'example',
|
|
80
233
|
workflowId: HotMesh.guid()
|
|
81
234
|
});
|
|
82
235
|
|
|
83
236
|
return await handle.result();
|
|
84
|
-
//returns '¡Hola, HotMesh!'
|
|
237
|
+
//returns ['Hello HotMesh', '¡Hola, HotMesh!']
|
|
85
238
|
}
|
|
86
239
|
```
|
|
87
|
-
|
|
88
|
-
|
|
240
|
+
|
|
241
|
+
4. Finally, create a **worker** and link the workflow function. Workers listen for tasks on their assigned Redis stream and invoke the workflow function each time they receive an event.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
89
244
|
//worker.ts
|
|
90
|
-
import {
|
|
245
|
+
import { MeshFlow } from '@hotmeshio/hotmesh';
|
|
91
246
|
import Redis from 'ioredis';
|
|
92
247
|
import * as workflows from './workflows';
|
|
93
248
|
|
|
94
249
|
async function run() {
|
|
95
|
-
const worker = await
|
|
250
|
+
const worker = await MeshFlow.Worker.create({
|
|
96
251
|
connection: {
|
|
97
252
|
class: Redis,
|
|
98
|
-
options: { host: '
|
|
253
|
+
options: { host: 'redis', port: 6379 },
|
|
99
254
|
},
|
|
100
255
|
taskQueue: 'default',
|
|
101
256
|
workflow: workflows.example,
|
|
@@ -104,222 +259,481 @@ HotMesh's *Durable* module is a TypeScript Library modeled after Temporal.io. If
|
|
|
104
259
|
await worker.run();
|
|
105
260
|
}
|
|
106
261
|
```
|
|
262
|
+
</details>
|
|
263
|
+
|
|
264
|
+
<details style="padding: .5em">
|
|
265
|
+
<summary style="font-size:1.25em;">Pause and wait for a signal</summary>
|
|
107
266
|
|
|
108
|
-
|
|
109
|
-
|
|
267
|
+
### Wait for Signal
|
|
268
|
+
Pause a function and only awaken when a matching signal is received from the outide.
|
|
110
269
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
270
|
+
1. Define the **workflow** logic. This one waits for the `my-sig-nal` signal, returning the signal payload (`{ hello: 'world' }`) when it eventually arrives. Interleave additional logic to meet your use case.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
//waitForWorkflow.ts
|
|
274
|
+
import { MeshFlow } from '@hotmeshio/hotmesh';
|
|
275
|
+
|
|
276
|
+
export async function waitForExample(): Promise<{hello: string}> {
|
|
277
|
+
return await MeshFlow.workflow.waitFor<{hello: string}>('my-sig-nal');
|
|
278
|
+
//continue processing, use the payload, etc...
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
2. Instance a HotMesh **client** and start a workflow. Use a custom workflow ID (`myWorkflow123`).
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
//client.ts
|
|
286
|
+
import { MeshFlow, HotMesh } from '@hotmeshio/hotmesh';
|
|
287
|
+
import Redis from 'ioredis';
|
|
288
|
+
|
|
289
|
+
async function run(): Promise<string> {
|
|
290
|
+
const client = new MeshFlow.Client({
|
|
291
|
+
connection: {
|
|
292
|
+
class: Redis,
|
|
293
|
+
options: { host: 'redis', port: 6379 }
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
//start a workflow; it will immediately pause
|
|
298
|
+
await client.workflow.start({
|
|
299
|
+
args: ['HotMesh'],
|
|
300
|
+
taskQueue: 'default',
|
|
301
|
+
workflowName: 'waitForExample',
|
|
302
|
+
workflowId: 'myWorkflow123',
|
|
303
|
+
await: false,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
118
306
|
```
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
307
|
+
|
|
308
|
+
3. Create a **worker** and link the `waitForExample` workflow function.
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
//worker.ts
|
|
312
|
+
import { MeshFlow } from '@hotmeshio/hotmesh';
|
|
313
|
+
import Redis from 'ioredis';
|
|
314
|
+
import * as workflows from './waitForWorkflow';
|
|
315
|
+
|
|
316
|
+
async function run() {
|
|
317
|
+
const worker = await MeshFlow.Worker.create({
|
|
318
|
+
connection: {
|
|
319
|
+
class: Redis,
|
|
320
|
+
options: { host: 'redis', port: 6379 },
|
|
321
|
+
},
|
|
322
|
+
taskQueue: 'default',
|
|
323
|
+
workflow: workflows.waitForExample,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
await worker.run();
|
|
327
|
+
}
|
|
122
328
|
```
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
329
|
+
|
|
330
|
+
4. Send a signal to awaken the paused function; await the function result.
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { MeshFlow } from '@hotmeshio/hotmesh';
|
|
334
|
+
import * as Redis from Redis;
|
|
335
|
+
|
|
336
|
+
const client = new MeshFlow.Client({
|
|
337
|
+
connection: {
|
|
338
|
+
class: Redis,
|
|
339
|
+
options: { host: 'redis', port: 6379 }
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
//awaken the function by sending a signal
|
|
344
|
+
await client.signal('my-sig-nal', { hello: 'world' });
|
|
345
|
+
|
|
346
|
+
//get the workflow handle and await the result
|
|
347
|
+
const handle = await client.getHandle({
|
|
127
348
|
taskQueue: 'default',
|
|
128
|
-
|
|
349
|
+
workflowId: 'myWorkflow123'
|
|
129
350
|
});
|
|
351
|
+
|
|
352
|
+
const result = await handle.result();
|
|
353
|
+
//returns { hello: 'world' }
|
|
130
354
|
```
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
355
|
+
</details>
|
|
356
|
+
|
|
357
|
+
<details style="padding: .5em">
|
|
358
|
+
<summary style="font-size:1.25em;">Wait for multiple signals (collation)</summary>
|
|
359
|
+
|
|
360
|
+
### Collate Multiple Signals
|
|
361
|
+
Use a standard `Promise` to collate and cache multiple signals. HotMesh will only awaken once **all** signals have arrived. HotMesh will track up to 25 concurrent signals.
|
|
362
|
+
|
|
363
|
+
1. Update the **workflow** logic to await two signals using a promise: `my-sig-nal-1` and `my-sig-nal-2`. Add additional logic to meet your use case.
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
//waitForWorkflows.ts
|
|
367
|
+
import { MeshFlow } from '@hotmeshio/hotmesh';
|
|
368
|
+
|
|
369
|
+
export async function waitForExample(): Promise<[boolean, number]> {
|
|
370
|
+
const [s1, s2] = await Promise.all([
|
|
371
|
+
Meshflow.workflow.waitFor<boolean>('my-sig-nal-1'),
|
|
372
|
+
Meshflow.workflow.waitFor<number>('my-sig-nal-2')
|
|
373
|
+
]);
|
|
374
|
+
//do something with the signal payloads (s1, s2)
|
|
375
|
+
return [s1, s2];
|
|
376
|
+
}
|
|
138
377
|
```
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
378
|
+
|
|
379
|
+
2. Send **two** signals to awaken the paused function.
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
import { MeshFlow } from '@hotmeshio/hotmesh';
|
|
383
|
+
import * as Redis from Redis;
|
|
384
|
+
|
|
385
|
+
const client = new MeshFlow.Client({
|
|
386
|
+
connection: {
|
|
387
|
+
class: Redis,
|
|
388
|
+
options: { host: 'redis', port: 6379 }
|
|
389
|
+
}
|
|
145
390
|
});
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
391
|
+
|
|
392
|
+
//send 2 signals to awaken the function; order is unimportant
|
|
393
|
+
await client.signal('my-sig-nal-2', 12345);
|
|
394
|
+
await client.signal('my-sig-nal-1', true);
|
|
395
|
+
|
|
396
|
+
//get the workflow handle and await the collated result
|
|
397
|
+
const handle = await client.getHandle({
|
|
151
398
|
taskQueue: 'default',
|
|
152
|
-
|
|
399
|
+
workflowId: 'myWorkflow123'
|
|
153
400
|
});
|
|
401
|
+
|
|
402
|
+
const result = await handle.result();
|
|
403
|
+
//returns [true, 12345]
|
|
154
404
|
```
|
|
155
|
-
|
|
156
|
-
```javascript
|
|
157
|
-
const context = await Durable.workflow.getContext();
|
|
158
|
-
```
|
|
159
|
-
- `search` Instance a search session
|
|
160
|
-
```javascript
|
|
161
|
-
const search = await Durable.workflow.search();
|
|
162
|
-
```
|
|
163
|
-
- `set` Set one or more name/value pairs
|
|
164
|
-
```javascript
|
|
165
|
-
await search.set('name1', 'value1', 'name2', 'value2');
|
|
166
|
-
```
|
|
167
|
-
- `get` Get a single value by name
|
|
168
|
-
```javascript
|
|
169
|
-
const value = await search.get('name');
|
|
170
|
-
```
|
|
171
|
-
- `mget` Get multiple values by name
|
|
172
|
-
```javascript
|
|
173
|
-
const [val1, val2] = await search.mget('name1', 'name2');
|
|
174
|
-
```
|
|
175
|
-
- `del` Delete one or more entries by name and return the number deleted
|
|
176
|
-
```javascript
|
|
177
|
-
const count = await search.del('name1', 'name2');
|
|
178
|
-
```
|
|
179
|
-
- `incr` Increment (or decrement) a number
|
|
180
|
-
```javascript
|
|
181
|
-
const value = await search.incr('name', 12);
|
|
182
|
-
```
|
|
183
|
-
- `mult` Multiply a number
|
|
184
|
-
```javascript
|
|
185
|
-
const value = await search.mult('name', 12);
|
|
186
|
-
```
|
|
405
|
+
</details>
|
|
187
406
|
|
|
188
|
-
|
|
407
|
+
<details style="padding: .5em">
|
|
408
|
+
<summary style="font-size:1.25em;">Create a recurring, cyclical workflow</summary>
|
|
189
409
|
|
|
190
|
-
###
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
app:
|
|
195
|
-
id: sandbox
|
|
196
|
-
version: '1'
|
|
197
|
-
graphs:
|
|
198
|
-
- subscribes: sandbox.work.do
|
|
199
|
-
publishes: sandbox.work.done
|
|
200
|
-
|
|
201
|
-
activities:
|
|
202
|
-
gateway:
|
|
203
|
-
type: trigger
|
|
204
|
-
servicec:
|
|
205
|
-
type: worker
|
|
206
|
-
topic: sandbox.work.do.servicec
|
|
207
|
-
serviced:
|
|
208
|
-
type: worker
|
|
209
|
-
topic: sandbox.work.do.serviced
|
|
210
|
-
sforcecloud:
|
|
211
|
-
type: worker
|
|
212
|
-
topic: sandbox.work.do.sforcecloud
|
|
213
|
-
|
|
214
|
-
transitions:
|
|
215
|
-
gateway:
|
|
216
|
-
- to: servicec
|
|
217
|
-
servicec:
|
|
218
|
-
- to: serviced
|
|
219
|
-
serviced:
|
|
220
|
-
- to: sforcecloud
|
|
221
|
-
```
|
|
410
|
+
### Cyclical Workflow
|
|
411
|
+
This example calls an activity and then sleeps for a week. It runs indefinitely until it's manually stopped. It takes advantage of durable execution and can safely sleep for months or years.
|
|
412
|
+
|
|
413
|
+
>Container restarts have no impact on actively executing workflows as all state is retained in Redis.
|
|
222
414
|
|
|
223
|
-
|
|
224
|
-
Provide your chosen Redis instance and configuration options to start a HotMesh Client. *HotMesh supports both `ioredis` and `redis` clients interchangeably.*
|
|
415
|
+
1. Define the **workflow** logic. This one calls a legacy `statusDiagnostic` function once a week.
|
|
225
416
|
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
import
|
|
417
|
+
```typescript
|
|
418
|
+
//recurringWorkflow.ts
|
|
419
|
+
import { MeshFlow } from '@hotmeshio/hotmesh';
|
|
420
|
+
import * as activities from './activities';
|
|
421
|
+
|
|
422
|
+
const { statusDiagnostic } = MeshFlow.workflow
|
|
423
|
+
.proxyActivities<typeof activities>({
|
|
424
|
+
activities
|
|
425
|
+
});
|
|
229
426
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
class: Redis,
|
|
235
|
-
options: { host, port, password, db } //per your chosen Redis client
|
|
427
|
+
export async function recurringExample(someValue: number): Promise<void> {
|
|
428
|
+
do {
|
|
429
|
+
await statusDiagnostic(someValue);
|
|
430
|
+
} while (await MeshFlow.workflow.sleepFor('1 week'));
|
|
236
431
|
}
|
|
237
|
-
|
|
238
|
-
});
|
|
239
|
-
```
|
|
432
|
+
```
|
|
240
433
|
|
|
241
|
-
|
|
434
|
+
2. Instance a HotMesh **client** and start a workflow. Assign a custom workflow ID (e.g., `myRecurring123`) if the workflow should be idempotent.
|
|
242
435
|
|
|
243
|
-
|
|
244
|
-
|
|
436
|
+
```typescript
|
|
437
|
+
//client.ts
|
|
438
|
+
import { MeshFlow, HotMesh } from '@hotmeshio/hotmesh';
|
|
439
|
+
import Redis from 'ioredis';
|
|
245
440
|
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
441
|
+
async function run(): Promise<string> {
|
|
442
|
+
const client = new MeshFlow.Client({
|
|
443
|
+
connection: {
|
|
444
|
+
class: Redis,
|
|
445
|
+
options: { host: 'redis', port: 6379 }
|
|
446
|
+
}
|
|
447
|
+
});
|
|
251
448
|
|
|
252
|
-
|
|
253
|
-
|
|
449
|
+
//start a workflow; it will immediately pause
|
|
450
|
+
await client.workflow.start({
|
|
451
|
+
args: [55],
|
|
452
|
+
taskQueue: 'default',
|
|
453
|
+
workflowName: 'recurringExample',
|
|
454
|
+
workflowId: 'myRecurring123',
|
|
455
|
+
await: false,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
```
|
|
254
459
|
|
|
255
|
-
|
|
256
|
-
await hotMesh.psub('sandbox.work.done.*', (topic, jobOutput) => {
|
|
257
|
-
// use jobOutput.data
|
|
258
|
-
});
|
|
259
|
-
```
|
|
460
|
+
3. Create a **worker** and link the `recurringExample` workflow function.
|
|
260
461
|
|
|
261
|
-
|
|
262
|
-
|
|
462
|
+
```typescript
|
|
463
|
+
//worker.ts
|
|
464
|
+
import { MeshFlow } from '@hotmeshio/hotmesh';
|
|
465
|
+
import Redis from 'ioredis';
|
|
466
|
+
import * as workflows from './recurringWorkflow';
|
|
263
467
|
|
|
264
|
-
|
|
265
|
-
const
|
|
266
|
-
|
|
468
|
+
async function run() {
|
|
469
|
+
const worker = await MeshFlow.Worker.create({
|
|
470
|
+
connection: {
|
|
471
|
+
class: Redis,
|
|
472
|
+
options: { host: 'redis', port: 6379 },
|
|
473
|
+
},
|
|
474
|
+
taskQueue: 'default',
|
|
475
|
+
workflow: workflows.recurringExample,
|
|
476
|
+
});
|
|
267
477
|
|
|
268
|
-
|
|
478
|
+
await worker.run();
|
|
479
|
+
}
|
|
480
|
+
```
|
|
269
481
|
|
|
270
|
-
|
|
271
|
-
Link worker functions to a topic of your choice. When a workflow activity in the YAML definition with a corresponding topic runs, HotMesh will invoke your function, retrying as configured until it succeeds.
|
|
482
|
+
4. Cancel the recurring workflow (`myRecurring123`) by calling `interrupt`.
|
|
272
483
|
|
|
273
|
-
```
|
|
274
|
-
import {
|
|
275
|
-
import Redis from
|
|
484
|
+
```typescript
|
|
485
|
+
import { MeshFlow } from '@hotmeshio/hotmesh';
|
|
486
|
+
import * as Redis from Redis;
|
|
276
487
|
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
workers: [
|
|
280
|
-
{
|
|
281
|
-
topic: 'sandbox.work.do.servicec',
|
|
282
|
-
redis: {
|
|
488
|
+
const client = new MeshFlow.Client({
|
|
489
|
+
connection: {
|
|
283
490
|
class: Redis,
|
|
284
|
-
options: { host, port
|
|
285
|
-
}
|
|
286
|
-
callback: async (data: StreamData) => {
|
|
287
|
-
return {
|
|
288
|
-
metadata: { ...data.metadata },
|
|
289
|
-
data: { }
|
|
290
|
-
};
|
|
491
|
+
options: { host: 'redis', port: 6379 }
|
|
291
492
|
}
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
//get the workflow handle and interrupt it
|
|
496
|
+
const handle = await client.getHandle({
|
|
497
|
+
taskQueue: 'default',
|
|
498
|
+
workflowId: 'myRecurring123'
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
const result = await handle.interrupt();
|
|
502
|
+
```
|
|
503
|
+
</details>
|
|
504
|
+
|
|
505
|
+
## MeshData | Transactional Analytics
|
|
506
|
+
The **MeshData** service extends the **MeshFlow** service, combining data record concepts and transactional workflow principles into a single *Operational Data Layer*.
|
|
507
|
+
|
|
508
|
+
Deployments with the Redis `FT.SEARCH` module enabled can use the **MeshData** module to merge [OLTP](https://en.wikipedia.org/wiki/Online_transaction_processing) and [OLAP](https://en.wikipedia.org/wiki/Online_analytical_processing) operations into a hybrid transactional/analytics ([HTAP](https://en.wikipedia.org/wiki/Hybrid_transactional/analytical_processing)) system.
|
|
509
|
+
|
|
510
|
+
*For those Redis deployments without the `FT.SEARCH` module, it's still useful to define a workflow schema. The MeshData class provides convenience methods for reading and writing hash field data to a workflow record (e.g., `get`, `del`, and `incr`).*
|
|
511
|
+
|
|
512
|
+
<details style="padding: .5em">
|
|
513
|
+
<summary style="font-size:1.25em;">Create a search index</summary>
|
|
514
|
+
|
|
515
|
+
### Workflow Data Indexes
|
|
516
|
+
|
|
517
|
+
This example demonstrates how to define a schema and deploy an index for a 'user' entity type.
|
|
518
|
+
|
|
519
|
+
1. Define the **schema** for the `user` entity. This one includes the 3 formats supported by the FT.SEARCH module: `TEXT`, `TAG` and `NUMERIC`.
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
//schema.ts
|
|
523
|
+
export const schema: Types.WorkflowSearchOptions = {
|
|
524
|
+
schema: {
|
|
525
|
+
id: { type: 'TAG', sortable: false },
|
|
526
|
+
first: { type: 'TEXT', sortable: false, nostem: true },
|
|
527
|
+
active: { type: 'TAG', sortable: false },
|
|
528
|
+
created: { type: 'NUMERIC', sortable: true },
|
|
529
|
+
},
|
|
530
|
+
index: 'user',
|
|
531
|
+
prefix: ['user'],
|
|
532
|
+
};
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
2. Create the Redis index upon server startup. This one initializes the 'user' index in Redis, using the schema defined in the previous step. It's OK to call `createSearchIndex` multiple times; it will only create the index if it doesn't already exist.
|
|
536
|
+
|
|
537
|
+
```typescript
|
|
538
|
+
//server.ts
|
|
539
|
+
import { MeshData } from '@hotmeshio/hotmesh';
|
|
540
|
+
import * as Redis from 'redis';
|
|
541
|
+
import { schema } from './schema';
|
|
542
|
+
|
|
543
|
+
const meshData = new MeshData(
|
|
544
|
+
Redis,
|
|
545
|
+
{ url: 'redis://:key_admin@redis:6379' },
|
|
546
|
+
schema,
|
|
547
|
+
);
|
|
548
|
+
await meshData.createSearchIndex('user', { namespace: 'meshdata' });
|
|
549
|
+
```
|
|
550
|
+
</details>
|
|
551
|
+
|
|
552
|
+
<details style="padding: .5em">
|
|
553
|
+
<summary style="font-size:1.25em;">Create an indexed, searchable record</summary>
|
|
554
|
+
|
|
555
|
+
### Workflow Record Data
|
|
556
|
+
This example demonstrates how to create a 'user' workflow backed by the searchable schema from the prior example.
|
|
557
|
+
|
|
558
|
+
1. Call MeshData `connect` to initialize a 'user' entity *worker*. It references a target worker function which will run the workflow. Data fields that are documented in the schema (like `active`) will be automatically indexed when set on the workflow record.
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
//connect.ts
|
|
562
|
+
import { MeshData } from '@hotmeshio/hotmesh';
|
|
563
|
+
import * as Redis from 'redis';
|
|
564
|
+
import { schema } from './schema';
|
|
565
|
+
|
|
566
|
+
export const connectUserWorker = async (): Promise<void> => {
|
|
567
|
+
const meshData = new MeshData(
|
|
568
|
+
Redis,
|
|
569
|
+
{ url: 'redis://:key_admin@redis:6379' },
|
|
570
|
+
schema,
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
await meshData.connect({
|
|
574
|
+
entity: 'user',
|
|
575
|
+
target: async function(name: string): Promise<string> {
|
|
576
|
+
//add custom, searchable data (`active`) and return
|
|
577
|
+
const search = await MeshData.workflow.search();
|
|
578
|
+
await search.set('active', 'yes');
|
|
579
|
+
return `Welcome, ${name}.`;
|
|
580
|
+
},
|
|
581
|
+
options: { namespace: 'meshdata' },
|
|
582
|
+
});
|
|
292
583
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
2. Wire up the worker at server startup, so it's ready to process incoming requests.
|
|
296
587
|
|
|
297
|
-
|
|
298
|
-
|
|
588
|
+
```typescript
|
|
589
|
+
//server.ts
|
|
590
|
+
import { connectUserWorker } from './connect';
|
|
591
|
+
await connectUserWorker();
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
3. Call MeshData `exec` to create a 'user' workflow. Searchable data can be set throughout the workflow's lifecycle. This one initializes the workflow with 3 data fields: `id`, `name` and `timestamp`. *An additional data field (`active`) is set within the workflow function in order to demonstrate both mechanisms for reading/writing data to a workflow.*
|
|
595
|
+
|
|
596
|
+
```typescript
|
|
597
|
+
//exec.ts
|
|
598
|
+
import { MeshData } from '@hotmeshio/hotmesh';
|
|
599
|
+
import * as Redis from 'redis';
|
|
600
|
+
|
|
601
|
+
const meshData = new MeshData(
|
|
602
|
+
Redis,
|
|
603
|
+
{ url: 'redis://:key_admin@redis:6379' },
|
|
604
|
+
schema,
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
export const newUser = async (id: string, name: string): Promise<string> => {
|
|
608
|
+
const response = await meshData.exec({
|
|
609
|
+
entity: 'user',
|
|
610
|
+
args: [name],
|
|
611
|
+
options: {
|
|
612
|
+
ttl: 'infinity',
|
|
613
|
+
id,
|
|
614
|
+
search: {
|
|
615
|
+
data: { id, name, timestamp: Date.now() }
|
|
616
|
+
},
|
|
617
|
+
namespace: 'meshdata',
|
|
618
|
+
},
|
|
619
|
+
});
|
|
620
|
+
return response;
|
|
621
|
+
};
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
4. Call the `newUser` function to create a searchable 'user' record.
|
|
625
|
+
|
|
626
|
+
```typescript
|
|
627
|
+
import { newUser } from './exec';
|
|
628
|
+
const response = await newUser('jim123', 'James');
|
|
629
|
+
```
|
|
630
|
+
</details>
|
|
631
|
+
|
|
632
|
+
<details style="padding: .5em">
|
|
633
|
+
<summary style="font-size:1.25em;">Fetch record data</summary>
|
|
634
|
+
|
|
635
|
+
### Read Record Data
|
|
636
|
+
This example demonstrates how to read data fields directly from a workflow.
|
|
637
|
+
|
|
638
|
+
1. Read data fields directly from the *jimbo123* 'user' record.
|
|
639
|
+
|
|
640
|
+
```typescript
|
|
641
|
+
//read.ts
|
|
642
|
+
import { MeshData } from '@hotmeshio/hotmesh';
|
|
643
|
+
import * as Redis from 'redis';
|
|
644
|
+
import { schema } from './schema';
|
|
645
|
+
|
|
646
|
+
const meshData = new MeshData(
|
|
647
|
+
Redis,
|
|
648
|
+
{ url: 'redis://:key_admin@redis:6379' },
|
|
649
|
+
schema,
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
const data = await meshData.get(
|
|
653
|
+
'user',
|
|
654
|
+
'jimbo123',
|
|
655
|
+
{
|
|
656
|
+
fields: ['id', 'name', 'timestamp', 'active'],
|
|
657
|
+
namespace: 'meshdata'
|
|
658
|
+
},
|
|
659
|
+
);
|
|
660
|
+
```
|
|
661
|
+
</details>
|
|
662
|
+
|
|
663
|
+
<details style="padding: .5em">
|
|
664
|
+
<summary style="font-size:1.25em;">Search record data</summary>
|
|
665
|
+
|
|
666
|
+
### Query Record Data
|
|
667
|
+
This example demonstrates how to search for those workflows where a given condition exists in the data. This one searches for active users. *NOTE: The native Redis FT.SEARCH syntax is supported. The JSON abstraction shown here is a convenience method for straight-forward, one-dimensional queries.*
|
|
668
|
+
|
|
669
|
+
1. Search for active users (where the value of the `active` field is `yes`).
|
|
670
|
+
|
|
671
|
+
```typescript
|
|
672
|
+
//read.ts
|
|
673
|
+
import { MeshData } from '@hotmeshio/hotmesh';
|
|
674
|
+
import * as Redis from 'redis';
|
|
675
|
+
import { schema } from './schema';
|
|
676
|
+
|
|
677
|
+
const meshData = new MeshData(
|
|
678
|
+
Redis,
|
|
679
|
+
{ url: 'redis://:key_admin@redis:6379' },
|
|
680
|
+
schema,
|
|
681
|
+
);
|
|
299
682
|
|
|
300
|
-
|
|
683
|
+
const results = await meshData.findWhere('user', {
|
|
684
|
+
query: [{ field: 'active', is: '=', value: 'yes' }],
|
|
685
|
+
limit: { start: 0, size: 100 },
|
|
686
|
+
return: ['id', 'name', 'timestamp', 'active']
|
|
687
|
+
});
|
|
688
|
+
```
|
|
689
|
+
</details>
|
|
690
|
+
|
|
691
|
+
## Visualize | OpenTelemetry
|
|
692
|
+
HotMesh's telemetry output provides unmatched insight into long-running, multi-service transactions. Add your Honeycomb credentials to any project using HotMesh and HotMesh will emit the full *OpenTelemetry* execution tree organized as a DAG.
|
|
693
|
+
|
|
694
|
+
<img src="./docs/img/visualize/opentelemetry.png" alt="Open Telemetry" style="width:600px;max-width:600px;">
|
|
695
|
+
|
|
696
|
+
## Visualize | HotMesh Dashboard
|
|
697
|
+
The HotMesh dashboard provides a detailed overview of all running workflows. As HotMesh is a service mesh, it's also possible to throttle and pause workers and engines attached to the mesh. Redis will simply inflate like a balloon until the throttle is removed and ingestion is resumed.
|
|
698
|
+
|
|
699
|
+
An LLM is included to simplify querying and analyzing workflow data for those deployments that include the Redis `FT.SEARCH` module.
|
|
700
|
+
|
|
701
|
+
<img src="./docs/img/visualize/hotmesh_dashboard.png" alt="HotMesh Dashboard" style="width:600px;max-width:600px;">
|
|
702
|
+
|
|
703
|
+
## Visualize | RedisInsight
|
|
704
|
+
View commands, streams, data, CPU, load, etc using the RedisInsight data browser.
|
|
705
|
+
|
|
706
|
+
<img src="./docs/img/visualize/redisinsight.png" alt="Redis Insight" style="width:600px;max-width:600px;">
|
|
707
|
+
|
|
708
|
+
## HotMesh
|
|
709
|
+
The *MeshData*, *MeshCall*, and *MeshFlow* modules are all created using the HotMesh modeling system. Refer to the following documents to better understand the platform and how it delivers workflow orchestration without a central application server.
|
|
710
|
+
|
|
711
|
+
### FAQ
|
|
301
712
|
Refer to the [FAQ](https://github.com/hotmeshio/sdk-typescript/tree/main/docs/faq.md) for terminology, definitions, and an exploration of how HotMesh facilitates orchestration use cases.
|
|
302
713
|
|
|
303
|
-
|
|
304
|
-
Refer to the [Quick Start](https://github.com/hotmeshio/sdk-typescript/tree/main/docs/quickstart.md) for sample YAML workflows you can copy, paste, and modify to get started.
|
|
714
|
+
### Quick Start
|
|
715
|
+
Refer to the [Quick Start](https://github.com/hotmeshio/sdk-typescript/tree/main/docs/quickstart.md) for sample YAML workflows you can copy, paste, and modify to get started with HotMesh.
|
|
305
716
|
|
|
306
|
-
|
|
717
|
+
### Developer Guide
|
|
307
718
|
For more details on the complete development process, including information about schemas, APIs, and deployment, consult the [Developer Guide](https://github.com/hotmeshio/sdk-typescript/tree/main/docs/developer_guide.md).
|
|
308
719
|
|
|
309
|
-
|
|
720
|
+
### Model Driven Development
|
|
310
721
|
[Model Driven Development](https://github.com/hotmeshio/sdk-typescript/tree/main/docs/model_driven_development.md) is an established strategy for managing process-oriented tasks. Check out this guide to understand its foundational principles.
|
|
311
722
|
|
|
312
|
-
|
|
723
|
+
### Data Mapping
|
|
313
724
|
Exchanging data between activities is central to HotMesh. For detailed information on supported functions and the functional mapping syntax (@pipes), see the [Data Mapping Overview](https://github.com/hotmeshio/sdk-typescript/tree/main/docs/data_mapping.md).
|
|
314
725
|
|
|
315
|
-
|
|
726
|
+
### Composition
|
|
316
727
|
While the simplest graphs are linear, detailing a consistent sequence of non-cyclical activities, graphs can be layered to represent intricate business scenarios. Some can even be designed to accommodate long-lasting workflows that span months. For more details, check out the [Composable Workflow Guide](https://github.com/hotmeshio/sdk-typescript/tree/main/docs/composable_workflow.md).
|
|
317
728
|
|
|
318
|
-
|
|
729
|
+
### System Lifecycle
|
|
319
730
|
Gain insight into HotMesh's monitoring, exception handling, and alarm configurations via the [System Lifecycle Guide](https://github.com/hotmeshio/sdk-typescript/tree/main/docs/system_lifecycle.md).
|
|
320
731
|
|
|
321
|
-
|
|
732
|
+
### Distributed Orchestration | System Overview
|
|
322
733
|
HotMesh is a distributed orchestration engine. Refer to the [Distributed Orchestration Guide](https://github.com/hotmeshio/sdk-typescript/tree/main/docs/distributed_orchestration.md) for a high-level overview of the approach.
|
|
323
734
|
|
|
324
|
-
|
|
735
|
+
### Distributed Orchestration | System Design
|
|
325
736
|
HotMesh is more than Redis and TypeScript. The theory that underlies the architecture is applicable to any number of data storage and streaming backends: [A Message-Oriented Approach to Decentralized Process Orchestration](https://zenodo.org/records/12168558).
|
|
737
|
+
|
|
738
|
+
### Samples
|
|
739
|
+
Refer to the [hotmeshio/samples-javascript](https://github.com/hotmeshio/samples-javascript) repo for usage examples.
|