@hotmeshio/hotmesh 0.0.9 → 0.0.10
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 +82 -76
- package/build/modules/key.d.ts +2 -2
- package/build/modules/key.js +3 -3
- package/build/package.json +1 -1
- package/build/services/durable/native.js +0 -1
- package/build/services/durable/worker.js +0 -1
- package/build/services/hotmesh/index.js +1 -1
- package/build/services/store/index.js +2 -2
- package/build/services/stream/clients/ioredis.js +1 -1
- package/build/services/stream/clients/redis.js +1 -1
- package/build/services/sub/clients/ioredis.js +1 -1
- package/build/services/sub/clients/redis.js +1 -1
- package/build/types/durable.d.ts +1 -1
- package/modules/key.ts +2 -2
- package/package.json +1 -1
- package/services/durable/native.ts +0 -1
- package/services/durable/worker.ts +0 -1
- package/services/hotmesh/index.ts +2 -2
- package/services/store/index.ts +3 -3
- package/services/stream/clients/ioredis.ts +2 -2
- package/services/stream/clients/redis.ts +2 -2
- package/services/sub/clients/ioredis.ts +2 -2
- package/services/sub/clients/redis.ts +2 -2
- package/types/durable.ts +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# HotMesh
|
|
2
2
|

|
|
3
3
|
|
|
4
|
-
Elevate Redis from an in-memory data store to a game-changing
|
|
4
|
+
Elevate Redis from an in-memory data store to a game-changing [service mesh](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/faq.md#what-is-hotmesh). Turn your unpredictable functions into unbreakable workflows.
|
|
5
5
|
|
|
6
6
|
## Install
|
|
7
7
|
[](https://badge.fury.io/js/%40hotmeshio%2Fhotmesh)
|
|
@@ -11,85 +11,91 @@ npm install @hotmeshio/hotmesh
|
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Design
|
|
14
|
-
HotMesh
|
|
14
|
+
The HotMesh SDK is designed to keep your code front-and-center. Write functions as you normally would, then use the HotMesh to make them durable.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
1. Start by defining **activities**. Activities are those functions that will be invoked by your workflow. They are commonly used to read and write to databases and invoke external services. They can be written in any style, using any framework, and can even be legacy functions you've already written. The only requirement is that they return a Promise.
|
|
17
|
+
```javascript
|
|
18
|
+
//activities.ts
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return `Hello, ${name}!`;
|
|
22
|
-
}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
>Next, define your workflow. Include conditional logic, loops, etc. It's vanilla code written in your own coding style--just make sure to call `proxyActivities` to run your activities durably.
|
|
26
|
-
|
|
27
|
-
**./workflows.ts**
|
|
28
|
-
```javascript
|
|
29
|
-
import { Durable } from '@hotmeshio/hotmesh';
|
|
30
|
-
import * as activities from './activities';
|
|
31
|
-
|
|
32
|
-
const { greet } = Durable.workflow
|
|
33
|
-
.proxyActivities<typeof activities>({
|
|
34
|
-
activities
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
export async function example(name: string): Promise<string> {
|
|
38
|
-
return await greet(name);
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
>Finally, create a worker and client. The *client* triggers workflows, while the *worker* runs them, retrying as necessary until the workflow succeeds--all without the need for complicated retry logic.
|
|
43
|
-
|
|
44
|
-
**./client.ts**
|
|
45
|
-
```javascript
|
|
46
|
-
import { Durable } from '@hotmeshio/hotmesh';
|
|
47
|
-
import Redis from 'ioredis'; //OR `import * as Redis from 'redis';`
|
|
48
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
49
|
-
|
|
50
|
-
async function run() {
|
|
51
|
-
const connection = await Durable.Connection.connect({
|
|
52
|
-
class: Redis,
|
|
53
|
-
options: { host: 'localhost', port: 6379 }
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const client = new Durable.Client({
|
|
57
|
-
connection
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const handle = await client.workflow.start({
|
|
61
|
-
args: ['HotMesh'],
|
|
62
|
-
taskQueue: 'hello-world',
|
|
63
|
-
workflowName: 'example',
|
|
64
|
-
workflowId: 'workflow-' + uuidv4()
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
console.log(await handle.result());
|
|
68
|
-
}
|
|
69
|
-
```
|
|
20
|
+
export async function greet(name: string): Promise<string> {
|
|
21
|
+
return `Hello, ${name}!`;
|
|
22
|
+
}
|
|
70
23
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
24
|
+
export async function saludar(nombre: string): Promise<string> {
|
|
25
|
+
return `¡Hola, ${nombre}!`;
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
2. Define your **workflow** logic. Include conditional branching, loops, etc to control activity execution. It's vanilla code written in your own coding style. The only requirement is to use `proxyActivities`, ensuring your activities are executed with HotMesh's durability guarantee.
|
|
29
|
+
```javascript
|
|
30
|
+
//workflows.ts
|
|
31
|
+
|
|
32
|
+
import { Durable } from '@hotmeshio/hotmesh';
|
|
33
|
+
import * as activities from './activities';
|
|
34
|
+
|
|
35
|
+
const { greet, saludar } = Durable.workflow
|
|
36
|
+
.proxyActivities<typeof activities>({
|
|
37
|
+
activities
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export async function example(name: string, lang: string): Promise<string> {
|
|
41
|
+
if (lang === 'es') {
|
|
42
|
+
return await saludar(name);
|
|
43
|
+
} else {
|
|
44
|
+
return await greet(name);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
3. Although you could call your workflow directly (it's just a vanilla function), it's only durable when invoked and orchestrated via HotMesh. By using a HotMesh **client** to send the request, the function is guaranteed to return a result.
|
|
50
|
+
```javascript
|
|
51
|
+
//client.ts
|
|
52
|
+
|
|
53
|
+
import { Durable } from '@hotmeshio/hotmesh';
|
|
54
|
+
import Redis from 'ioredis'; //OR `import * as Redis from 'redis';`
|
|
55
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
56
|
+
|
|
57
|
+
async function run(): Promise<string> {
|
|
58
|
+
const client = new Durable.Client({
|
|
59
|
+
connection: {
|
|
60
|
+
class: Redis,
|
|
61
|
+
options: { host: 'localhost', port: 6379 }
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const handle = await client.workflow.start({
|
|
66
|
+
args: ['HotMesh', 'es'],
|
|
67
|
+
taskQueue: 'hello-world',
|
|
68
|
+
workflowName: 'example',
|
|
69
|
+
workflowId: uuidv4()
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return await handle.result();
|
|
73
|
+
//returns '¡Hola, HotMesh!'
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
4. The last step is to create a **worker** that executes the workflow. Workers listen for tasks on their assigned channel, executing their assigned workflow function until it succeeds.
|
|
78
|
+
```javascript
|
|
79
|
+
//worker.ts
|
|
80
|
+
|
|
81
|
+
import { Durable } from '@hotmeshio/hotmesh';
|
|
82
|
+
import Redis from 'ioredis';
|
|
83
|
+
import * as workflows from './workflows';
|
|
84
|
+
|
|
85
|
+
async function run() {
|
|
86
|
+
const worker = await Durable.Worker.create({
|
|
87
|
+
connection: {
|
|
88
|
+
class: Redis,
|
|
89
|
+
options: { host: 'localhost', port: 6379 },
|
|
90
|
+
},
|
|
91
|
+
taskQueue: 'hello-world',
|
|
92
|
+
workflow: workflows.example,
|
|
93
|
+
});
|
|
94
|
+
await worker.run();
|
|
95
|
+
}
|
|
96
|
+
```
|
|
91
97
|
|
|
92
|
-
>HotMesh delivers durable
|
|
98
|
+
>HotMesh delivers durable workflows without the cost and complexity of a centralized service mesh. Refer to the [samples-typescript](https://github.com/hotmeshio/samples-typescript) Git Repo for a range of examples, including compositional workflows (where one workflow calls another) and remote execution (where calls are brokered across microservices).
|
|
93
99
|
|
|
94
100
|
## Advanced Design
|
|
95
101
|
HotMesh's TypeScript SDK is the easiest way to make your functions durable. But if you need full control over your function lifecycles (including high-volume, high-speed use cases), you can use HotMesh's underlying YAML models to optimize your durable workflows. The following model depicts a sequence of activities orchestrated by HotMesh. Any function you associate with a `topic` in your YAML definition is guaranteed to be durable.
|
package/build/modules/key.d.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* hmsh:<appid>:sym:keys:<activityid|$subscribes> -> {hash} list of symbols based upon schema enums (initially) and adaptively optimized (later) during runtime; if '$subscribes' is used as the activityid, it is a top-level `job` symbol set (for job keys)
|
|
26
26
|
* hmsh:<appid>:sym:vals: -> {hash} list of symbols for job values across all app versions
|
|
27
27
|
*/
|
|
28
|
-
declare const
|
|
28
|
+
declare const HMNS = "hmsh";
|
|
29
29
|
declare enum KeyType {
|
|
30
30
|
APP = 0,
|
|
31
31
|
ENGINE_ID = 1,
|
|
@@ -72,4 +72,4 @@ declare class KeyService {
|
|
|
72
72
|
*/
|
|
73
73
|
static mintKey(namespace: string, keyType: KeyType, params: KeyStoreParams): string;
|
|
74
74
|
}
|
|
75
|
-
export { KeyService, KeyType, KeyStoreParams,
|
|
75
|
+
export { KeyService, KeyType, KeyStoreParams, HMNS };
|
package/build/modules/key.js
CHANGED
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
* hmsh:<appid>:sym:vals: -> {hash} list of symbols for job values across all app versions
|
|
28
28
|
*/
|
|
29
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.
|
|
30
|
+
exports.HMNS = exports.KeyType = exports.KeyService = void 0;
|
|
31
31
|
//default namespace for hotmesh
|
|
32
|
-
const
|
|
33
|
-
exports.
|
|
32
|
+
const HMNS = "hmsh";
|
|
33
|
+
exports.HMNS = HMNS;
|
|
34
34
|
//these are the entity types that are stored in the key/value store
|
|
35
35
|
var KeyType;
|
|
36
36
|
(function (KeyType) {
|
package/build/package.json
CHANGED
|
@@ -17,7 +17,7 @@ class HotMeshService {
|
|
|
17
17
|
}
|
|
18
18
|
verifyAndSetNamespace(namespace) {
|
|
19
19
|
if (!namespace) {
|
|
20
|
-
this.namespace = key_1.
|
|
20
|
+
this.namespace = key_1.HMNS;
|
|
21
21
|
}
|
|
22
22
|
else if (!namespace.match(/^[A-Za-z0-9-]+$/)) {
|
|
23
23
|
throw new Error(`config.namespace [${namespace}] is invalid`);
|
|
@@ -58,7 +58,7 @@ class StoreService {
|
|
|
58
58
|
};
|
|
59
59
|
this.redisClient = redisClient;
|
|
60
60
|
}
|
|
61
|
-
async init(namespace = key_1.
|
|
61
|
+
async init(namespace = key_1.HMNS, appId, logger) {
|
|
62
62
|
this.namespace = namespace;
|
|
63
63
|
this.appId = appId;
|
|
64
64
|
this.logger = logger;
|
|
@@ -111,7 +111,7 @@ class StoreService {
|
|
|
111
111
|
if (bCreate) {
|
|
112
112
|
const packageJson = await Promise.resolve().then(() => __importStar(require('../../package.json')));
|
|
113
113
|
const version = packageJson['version'] || '0.0.0';
|
|
114
|
-
settings = { namespace: key_1.
|
|
114
|
+
settings = { namespace: key_1.HMNS, version };
|
|
115
115
|
await this.setSettings(settings);
|
|
116
116
|
return settings;
|
|
117
117
|
}
|
|
@@ -7,7 +7,7 @@ class IORedisStreamService extends index_1.StreamService {
|
|
|
7
7
|
constructor(redisClient) {
|
|
8
8
|
super(redisClient);
|
|
9
9
|
}
|
|
10
|
-
async init(namespace = key_1.
|
|
10
|
+
async init(namespace = key_1.HMNS, appId, logger) {
|
|
11
11
|
this.namespace = namespace;
|
|
12
12
|
this.logger = logger;
|
|
13
13
|
this.appId = appId;
|
|
@@ -7,7 +7,7 @@ class RedisStreamService extends index_1.StreamService {
|
|
|
7
7
|
constructor(redisClient) {
|
|
8
8
|
super(redisClient);
|
|
9
9
|
}
|
|
10
|
-
async init(namespace = key_1.
|
|
10
|
+
async init(namespace = key_1.HMNS, appId, logger) {
|
|
11
11
|
this.namespace = namespace;
|
|
12
12
|
this.logger = logger;
|
|
13
13
|
this.appId = appId;
|
|
@@ -7,7 +7,7 @@ class IORedisSubService extends index_1.SubService {
|
|
|
7
7
|
constructor(redisClient) {
|
|
8
8
|
super(redisClient);
|
|
9
9
|
}
|
|
10
|
-
async init(namespace = key_1.
|
|
10
|
+
async init(namespace = key_1.HMNS, appId, engineId, logger) {
|
|
11
11
|
this.namespace = namespace;
|
|
12
12
|
this.logger = logger;
|
|
13
13
|
this.appId = appId;
|
|
@@ -7,7 +7,7 @@ class RedisSubService extends index_1.SubService {
|
|
|
7
7
|
constructor(redisClient) {
|
|
8
8
|
super(redisClient);
|
|
9
9
|
}
|
|
10
|
-
async init(namespace = key_1.
|
|
10
|
+
async init(namespace = key_1.HMNS, appId, engineId, logger) {
|
|
11
11
|
this.namespace = namespace;
|
|
12
12
|
this.logger = logger;
|
|
13
13
|
this.appId = appId;
|
package/build/types/durable.d.ts
CHANGED
package/modules/key.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
//default namespace for hotmesh
|
|
30
|
-
const
|
|
30
|
+
const HMNS = "hmsh";
|
|
31
31
|
|
|
32
32
|
//these are the entity types that are stored in the key/value store
|
|
33
33
|
enum KeyType {
|
|
@@ -126,4 +126,4 @@ class KeyService {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
export { KeyService, KeyType, KeyStoreParams,
|
|
129
|
+
export { KeyService, KeyType, KeyStoreParams, HMNS };
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
-
import {
|
|
2
|
+
import { HMNS } from '../../modules/key';
|
|
3
3
|
import { EngineService } from '../engine';
|
|
4
4
|
import { LoggerService, ILogger } from '../logger';
|
|
5
5
|
import { StreamSignaler } from '../signaler/stream';
|
|
@@ -33,7 +33,7 @@ class HotMeshService {
|
|
|
33
33
|
|
|
34
34
|
verifyAndSetNamespace(namespace?: string) {
|
|
35
35
|
if (!namespace) {
|
|
36
|
-
this.namespace =
|
|
36
|
+
this.namespace = HMNS;
|
|
37
37
|
} else if (!namespace.match(/^[A-Za-z0-9-]+$/)) {
|
|
38
38
|
throw new Error(`config.namespace [${namespace}] is invalid`);
|
|
39
39
|
} else {
|
package/services/store/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
KeyService,
|
|
3
3
|
KeyStoreParams,
|
|
4
4
|
KeyType,
|
|
5
|
-
|
|
5
|
+
HMNS} from '../../modules/key';
|
|
6
6
|
import { ILogger } from '../logger';
|
|
7
7
|
import { MDATA_SYMBOLS, SerializerService as Serializer } from '../serializer';
|
|
8
8
|
import { Cache } from './cache';
|
|
@@ -119,7 +119,7 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
119
119
|
this.redisClient = redisClient;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
async init(namespace =
|
|
122
|
+
async init(namespace = HMNS, appId: string, logger: ILogger): Promise<HotMeshApps> {
|
|
123
123
|
this.namespace = namespace;
|
|
124
124
|
this.appId = appId;
|
|
125
125
|
this.logger = logger;
|
|
@@ -178,7 +178,7 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
178
178
|
if (bCreate) {
|
|
179
179
|
const packageJson = await import('../../package.json');
|
|
180
180
|
const version: string = packageJson['version'] || '0.0.0';
|
|
181
|
-
settings = { namespace:
|
|
181
|
+
settings = { namespace: HMNS, version } as HotMeshSettings;
|
|
182
182
|
await this.setSettings(settings);
|
|
183
183
|
return settings;
|
|
184
184
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KeyService, KeyStoreParams, KeyType,
|
|
1
|
+
import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
|
|
2
2
|
import { ILogger } from '../../logger';
|
|
3
3
|
import { StreamService } from '../index';
|
|
4
4
|
import { RedisClientType, RedisMultiType } from '../../../types/ioredisclient';
|
|
@@ -14,7 +14,7 @@ class IORedisStreamService extends StreamService<RedisClientType, RedisMultiType
|
|
|
14
14
|
super(redisClient);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
async init(namespace =
|
|
17
|
+
async init(namespace = HMNS, appId: string, logger: ILogger): Promise<void> {
|
|
18
18
|
this.namespace = namespace;
|
|
19
19
|
this.logger = logger;
|
|
20
20
|
this.appId = appId;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KeyService, KeyStoreParams, KeyType,
|
|
1
|
+
import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
|
|
2
2
|
import { ILogger } from '../../logger';
|
|
3
3
|
import { StreamService } from '../index';
|
|
4
4
|
import { RedisClientType, RedisMultiType } from '../../../types/redisclient';
|
|
@@ -14,7 +14,7 @@ class RedisStreamService extends StreamService<RedisClientType, RedisMultiType>
|
|
|
14
14
|
super(redisClient);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
async init(namespace =
|
|
17
|
+
async init(namespace = HMNS, appId: string, logger: ILogger): Promise<void> {
|
|
18
18
|
this.namespace = namespace;
|
|
19
19
|
this.logger = logger;
|
|
20
20
|
this.appId = appId;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KeyService, KeyStoreParams, KeyType,
|
|
1
|
+
import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
|
|
2
2
|
import { ILogger } from '../../logger';
|
|
3
3
|
import { SubService } from '../index';
|
|
4
4
|
import { RedisClientType, RedisMultiType } from '../../../types/ioredisclient';
|
|
@@ -14,7 +14,7 @@ class IORedisSubService extends SubService<RedisClientType, RedisMultiType> {
|
|
|
14
14
|
super(redisClient);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
async init(namespace =
|
|
17
|
+
async init(namespace = HMNS, appId: string, engineId: string, logger: ILogger): Promise<void> {
|
|
18
18
|
this.namespace = namespace;
|
|
19
19
|
this.logger = logger;
|
|
20
20
|
this.appId = appId;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KeyService, KeyStoreParams, KeyType,
|
|
1
|
+
import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
|
|
2
2
|
import { ILogger } from '../../logger';
|
|
3
3
|
import { SubService } from '../index';
|
|
4
4
|
import { RedisClientType, RedisMultiType } from '../../../types/redisclient';
|
|
@@ -14,7 +14,7 @@ class RedisSubService extends SubService<RedisClientType, RedisMultiType> {
|
|
|
14
14
|
super(redisClient);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
async init(namespace =
|
|
17
|
+
async init(namespace = HMNS, appId: string, engineId: string, logger: ILogger): Promise<void> {
|
|
18
18
|
this.namespace = namespace;
|
|
19
19
|
this.logger = logger;
|
|
20
20
|
this.appId = appId;
|
package/types/durable.ts
CHANGED
|
@@ -39,7 +39,7 @@ type Registry = {
|
|
|
39
39
|
|
|
40
40
|
type WorkerConfig = {
|
|
41
41
|
connection: Connection;
|
|
42
|
-
namespace
|
|
42
|
+
namespace?: string; //`appid` in the YAML (e.g, 'default')
|
|
43
43
|
taskQueue: string; //`subscribes` in the YAML (e.g, 'hello-world')
|
|
44
44
|
workflow: Function //target function to run
|
|
45
45
|
}
|