@hotmeshio/hotmesh 0.0.26 → 0.0.27

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 CHANGED
@@ -3,9 +3,11 @@
3
3
 
4
4
  Elevate Redis from an in-memory data cache, and turn your unpredictable functions into unbreakable workflows.
5
5
 
6
- **HotMesh** is a wrapper for Redis that exposes a higher level set of domain constructs like ‘activities’, ‘workflows’, 'jobs', etc. Behind the scenes, it uses *Redis Data* (Hash, ZSet, List); *Redis Streams* (XReadGroup, XAdd, XLen, etc); and *Redis Publish/Subscribe*.
6
+ **HotMesh** is a wrapper for Redis that exposes concepts like ‘activities’, ‘workflows’, and 'jobs'. Behind the scenes, it uses *Redis Data* (Hash, ZSet, List); *Redis Streams* (XReadGroup, XAdd, XLen, etc); and *Redis Publish/Subscribe*.
7
7
 
8
- It's still Redis in the background, but the information flow is reversed. Instead of your functions calling Redis (e.g., for caching a document), Redis governs your function execution. If your microservice container goes down or your function simply fails, HotMesh will restore function state at the point of failure and retry until it succeeds.
8
+ It's still Redis in the background, but your functions are run as *reentrant processes* and are executed in a distributed environment, with all the benefits of a distributed system, including fault tolerance, scalability, and high availability.
9
+
10
+ Write functions in your own preferred style, and let Redis govern their execution at its unmatched scale and performance.
9
11
 
10
12
  ## Install
11
13
  [![npm version](https://badge.fury.io/js/%40hotmeshio%2Fhotmesh.svg)](https://badge.fury.io/js/%40hotmeshio%2Fhotmesh)
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -206,16 +206,16 @@ class MeshOSService {
206
206
  return `${prefixedFieldName}:"${value}"`;
207
207
  case 'NUMERIC':
208
208
  let range = '';
209
- if (is.startsWith('=')) {
209
+ if (is.startsWith('=')) { //equal
210
210
  range = `[${value} ${value}]`;
211
211
  }
212
- else if (is === '<') {
212
+ else if (is.startsWith('<')) { //less than or equal
213
213
  range = `[-inf ${value}]`;
214
214
  }
215
- else if (is === '>') {
215
+ else if (is.startsWith('>')) { //greater than or equal
216
216
  range = `[${value} +inf]`;
217
217
  }
218
- else if (is === '[]') {
218
+ else if (is === '[]') { //between
219
219
  range = `[${value[0]} ${value[1]}]`;
220
220
  }
221
221
  return `${prefixedFieldName}:${range}`;
@@ -23,9 +23,10 @@ export declare class Search {
23
23
  * calling any method that produces side effects (changes the value)
24
24
  */
25
25
  getSearchSessionGuid(): string;
26
- set(key: string, value: string): Promise<void>;
26
+ set(...args: string[]): Promise<void>;
27
27
  get(key: string): Promise<string>;
28
- del(key: string): Promise<void>;
28
+ mget(...args: string[]): Promise<string[]>;
29
+ del(...args: string[]): Promise<number | void>;
29
30
  incr(key: string, val: number): Promise<number>;
30
31
  mult(key: string, val: number): Promise<number>;
31
32
  }
@@ -59,12 +59,17 @@ class Search {
59
59
  //return the search session as it would exist in the search session index
60
60
  return `${this.searchSessionId}-${this.searchSessionIndex++}-`;
61
61
  }
62
- async set(key, value) {
62
+ async set(...args) {
63
63
  const ssGuid = this.getSearchSessionGuid();
64
64
  const ssGuidValue = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, ssGuid, '1'));
65
65
  if (ssGuidValue === 1) {
66
- //only allowed to set a value the first time
67
- await this.store.exec('HSET', this.jobId, this.safeKey(key), value.toString());
66
+ const safeArgs = [];
67
+ for (let i = 0; i < args.length; i += 2) {
68
+ const key = this.safeKey(args[i]);
69
+ const value = args[i + 1].toString();
70
+ safeArgs.push(key, value);
71
+ }
72
+ await this.store.exec('HSET', this.jobId, ...safeArgs);
68
73
  }
69
74
  }
70
75
  async get(key) {
@@ -76,11 +81,29 @@ class Search {
76
81
  return '';
77
82
  }
78
83
  }
79
- async del(key) {
84
+ async mget(...args) {
85
+ const safeArgs = [];
86
+ for (let i = 0; i < args.length; i++) {
87
+ safeArgs.push(this.safeKey(args[i]));
88
+ }
89
+ try {
90
+ return await this.store.exec('HMGET', this.jobId, ...safeArgs);
91
+ }
92
+ catch (err) {
93
+ this.hotMeshClient.logger.error('durable-search-mget-error', { err });
94
+ return [];
95
+ }
96
+ }
97
+ async del(...args) {
80
98
  const ssGuid = this.getSearchSessionGuid();
81
99
  const ssGuidValue = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, ssGuid, '1'));
82
100
  if (ssGuidValue === 1) {
83
- await this.store.exec('HDEL', this.jobId, this.safeKey(key));
101
+ const safeArgs = [];
102
+ for (let i = 0; i < args.length; i++) {
103
+ safeArgs.push(this.safeKey(args[i]));
104
+ }
105
+ const response = await this.store.exec('HDEL', this.jobId, ...safeArgs);
106
+ return isNaN(response) ? undefined : Number(response);
84
107
  }
85
108
  }
86
109
  async incr(key, val) {
@@ -84,7 +84,7 @@ type WorkerConfig = {
84
84
  };
85
85
  type FindWhereQuery = {
86
86
  field: string;
87
- is: string;
87
+ is: '=' | '==' | '>=' | '<=' | '[]';
88
88
  value: string | boolean | number | [number, number];
89
89
  type?: string;
90
90
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -309,13 +309,13 @@ export class MeshOSService {
309
309
  return `${prefixedFieldName}:"${value}"`;
310
310
  case 'NUMERIC':
311
311
  let range = '';
312
- if (is.startsWith('=')) {
312
+ if (is.startsWith('=')) { //equal
313
313
  range = `[${value} ${value}]`;
314
- } else if (is === '<') {
314
+ } else if (is.startsWith('<')) { //less than or equal
315
315
  range = `[-inf ${value}]`;
316
- } else if (is === '>') {
316
+ } else if (is.startsWith('>')) { //greater than or equal
317
317
  range = `[${value} +inf]`;
318
- } else if (is === '[]') {
318
+ } else if (is === '[]') { //between
319
319
  range = `[${value[0]} ${value[1]}]`
320
320
  }
321
321
  return `${prefixedFieldName}:${range}`;
@@ -69,12 +69,17 @@ export class Search {
69
69
  return `${this.searchSessionId}-${this.searchSessionIndex++}-`;
70
70
  }
71
71
 
72
- async set(key: string, value: string): Promise<void> {
72
+ async set(...args: string[]): Promise<void> {
73
73
  const ssGuid = this.getSearchSessionGuid();
74
74
  const ssGuidValue = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, ssGuid, '1') as string);
75
75
  if (ssGuidValue === 1) {
76
- //only allowed to set a value the first time
77
- await this.store.exec('HSET', this.jobId, this.safeKey(key), value.toString());
76
+ const safeArgs: string[] = [];
77
+ for (let i = 0; i < args.length; i += 2) {
78
+ const key = this.safeKey(args[i]);
79
+ const value = args[i+1].toString();
80
+ safeArgs.push(key, value);
81
+ }
82
+ await this.store.exec('HSET', this.jobId, ...safeArgs);
78
83
  }
79
84
  }
80
85
 
@@ -87,11 +92,29 @@ export class Search {
87
92
  }
88
93
  }
89
94
 
90
- async del(key: string): Promise<void> {
95
+ async mget(...args: string[]): Promise<string[]> {
96
+ const safeArgs: string[] = [];
97
+ for (let i = 0; i < args.length; i++) {
98
+ safeArgs.push(this.safeKey(args[i]));
99
+ }
100
+ try {
101
+ return await this.store.exec('HMGET', this.jobId, ...safeArgs) as string[];
102
+ } catch (err) {
103
+ this.hotMeshClient.logger.error('durable-search-mget-error', { err });
104
+ return [];
105
+ }
106
+ }
107
+
108
+ async del(...args: string[]): Promise<number | void> {
91
109
  const ssGuid = this.getSearchSessionGuid();
92
110
  const ssGuidValue = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, ssGuid, '1') as string);
93
111
  if (ssGuidValue === 1) {
94
- await this.store.exec('HDEL', this.jobId, this.safeKey(key));
112
+ const safeArgs: string[] = [];
113
+ for (let i = 0; i < args.length; i++) {
114
+ safeArgs.push(this.safeKey(args[i]));
115
+ }
116
+ const response = await this.store.exec('HDEL', this.jobId, ...safeArgs);
117
+ return isNaN(response as unknown as number) ? undefined : Number(response);
95
118
  }
96
119
  }
97
120
 
package/types/durable.ts CHANGED
@@ -95,7 +95,7 @@ type WorkerConfig = {
95
95
 
96
96
  type FindWhereQuery = {
97
97
  field: string;
98
- is: string;
98
+ is: '=' | '==' | '>=' | '<=' | '[]';
99
99
  value: string | boolean | number | [number, number];
100
100
  type?: string; //default is TEXT
101
101
  }