Skip to content

Commit 2ec3797

Browse files
committed
refactor: made generic cache async
1 parent 6a202d4 commit 2ec3797

File tree

3 files changed

+71
-71
lines changed

3 files changed

+71
-71
lines changed

packages/actor-memory-expression/src/memory_calculator.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,16 @@ const processTemplateVariables = (defaultMemoryMbytes: string): string => {
178178
* @param cache An optional cache to store/retrieve compiled expressions.
179179
* @returns The compiled EvalFunction.
180180
*/
181-
const getCompiledExpression = (expression: string, cache: CompilationCache | undefined): EvalFunction => {
181+
const getCompiledExpression = async (expression: string, cache: CompilationCache | undefined): Promise<EvalFunction> => {
182182
if (!cache) {
183183
return compile(expression);
184184
}
185185

186-
let compiledExpression = cache.get(expression);
186+
let compiledExpression = await cache.get(expression);
187187

188188
if (!compiledExpression) {
189189
compiledExpression = compile(expression);
190-
cache.set(expression, compiledExpression!);
190+
await cache.set(expression, compiledExpression!);
191191
}
192192

193193
return compiledExpression;
@@ -202,7 +202,7 @@ const getCompiledExpression = (expression: string, cache: CompilationCache | und
202202
* @param options.cache Optional synchronous cache. Since compiled functions cannot be saved to a database/Redis, they are kept in local memory.
203203
* @returns The calculated memory value rounded to the closest power of 2 and clamped within allowed limits.
204204
*/
205-
export const calculateRunDynamicMemory = (
205+
export const calculateRunDynamicMemory = async (
206206
defaultMemoryMbytes: string,
207207
context: MemoryEvaluationContext,
208208
options: { cache: CompilationCache } | undefined = undefined,
@@ -220,7 +220,7 @@ export const calculateRunDynamicMemory = (
220220
get: customGetFunc,
221221
};
222222

223-
const compiledExpression = getCompiledExpression(preprocessedExpression, options?.cache);
223+
const compiledExpression = await getCompiledExpression(preprocessedExpression, options?.cache);
224224

225225
let finalResult: number | { entries: number[] } = compiledExpression.evaluate(preparedContext);
226226

packages/actor-memory-expression/src/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export type MemoryEvaluationContext = {
1616
}
1717

1818
export type CompilationCache = {
19-
get: (expression: string) => EvalFunction | null;
20-
set: (expression: string, compilationResult: EvalFunction) => void;
21-
length: () => number;
19+
get: (expression: string) => Promise<EvalFunction | null>;
20+
set: (expression: string, compilationResult: EvalFunction) => Promise<void>;
21+
length: () => Promise<number>;
2222
}

test/memory_calculator.test.ts

Lines changed: 63 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,49 @@ describe('calculateDefaultMemoryFromExpression', () => {
88
const emptyContext = { input: {}, runOptions: {} };
99

1010
describe('Basic evaluation', () => {
11-
it('correctly calculates and rounds memory from one-line expression', () => {
11+
it('correctly calculates and rounds memory from one-line expression', async () => {
1212
const context = { input: { size: 10 }, runOptions: {} };
1313
// 10 * 1024 = 10240. log2(10240) ~ 13.32. round(13.32) -> 2^13 = 8192
14-
const result = calculateRunDynamicMemory('input.size * 1024', context);
14+
const result = await calculateRunDynamicMemory('input.size * 1024', context);
1515
expect(result).toBe(8192);
1616
});
1717

18-
it('correctly calculates and rounds memory from multi-line expression', () => {
18+
it('correctly calculates and rounds memory from multi-line expression', async () => {
1919
const context = { input: { base: 10, multiplier: 1024 }, runOptions: {} };
2020
const expr = `
2121
baseVal = input.base;
2222
multVal = input.multiplier;
2323
baseVal * multVal
2424
`;
2525
// 10 * 1024 = 10240. Rounds to 8192.
26-
const result = calculateRunDynamicMemory(expr, context);
26+
const result = await calculateRunDynamicMemory(expr, context);
2727
expect(result).toBe(8192);
2828
});
2929

30-
it('correctly accesses runOptions from the context', () => {
30+
it('correctly accesses runOptions from the context', async () => {
3131
const context = { input: {}, runOptions: { timeoutSecs: 60 } };
3232
const expr = 'runOptions.timeoutSecs * 100'; // 60 * 100 = 6000
3333
// log2(6000) ~ 12.55. round(13) -> 2^13 = 8192
34-
const result = calculateRunDynamicMemory(expr, context);
34+
const result = await calculateRunDynamicMemory(expr, context);
3535
expect(result).toBe(8192);
3636
});
3737

38-
it('correctly handles a single number expression', () => {
39-
const result = calculateRunDynamicMemory('2048', emptyContext);
38+
it('correctly handles a single number expression', async () => {
39+
const result = await calculateRunDynamicMemory('2048', emptyContext);
4040
expect(result).toBe(2048);
4141
});
4242

43-
it('correctly handles expressions with custom get() function', () => {
43+
it('correctly handles expressions with custom get() function', async () => {
4444
const context = { input: { nested: { value: 20 } }, runOptions: {} };
4545
const expr = "get(input, 'nested.value', 10) * 50"; // 20 * 50 = 1000
46-
const result = calculateRunDynamicMemory(expr, context);
46+
const result = await calculateRunDynamicMemory(expr, context);
4747
expect(result).toBe(1024);
4848
});
4949

50-
it('should use get() default value when path is invalid', () => {
50+
it('should use get() default value when path is invalid', async () => {
5151
const context = { input: { user: {} }, runOptions: {} };
5252
const expr = "get(input, 'user.settings.memory', 512)";
53-
const result = calculateRunDynamicMemory(expr, context);
53+
const result = await calculateRunDynamicMemory(expr, context);
5454
expect(result).toBe(512);
5555
});
5656

@@ -78,10 +78,10 @@ describe('calculateDefaultMemoryFromExpression', () => {
7878

7979
it.each(cases)(
8080
`supports operation '$name'`,
81-
({ expression, result }) => {
81+
async ({ expression, result }) => {
8282
// in case operation is not supported, mathjs will throw
8383
// we round the result to the closest power of 2 and clamp within limits.
84-
expect(calculateRunDynamicMemory(expression, context)).toBe(result);
84+
expect(await calculateRunDynamicMemory(expression, context)).toBe(result);
8585
},
8686
);
8787
});
@@ -108,100 +108,100 @@ describe('calculateDefaultMemoryFromExpression', () => {
108108

109109
it.each(cases)(
110110
`supports operation '$name'`,
111-
({ expression, error }) => {
111+
async ({ expression, error }) => {
112112
// in case operation is not supported, mathjs will throw
113113
// we round the result to the closest power of 2 and clamp within limits.
114-
expect(() => calculateRunDynamicMemory(expression, context)).toThrow(error);
114+
await expect(calculateRunDynamicMemory(expression, context)).rejects.toThrow(error);
115115
},
116116
);
117117
});
118118
});
119119

120120
describe('Template {{variables}} support', () => {
121-
it('should throw error if variable doesn\'t start with runOptions. or input.', () => {
121+
it('should throw error if variable doesn\'t start with runOptions. or input.', async () => {
122122
const context = { input: {}, runOptions: { memoryMbytes: 16 } };
123123
const expr = '{{nonexistentVariable}} * 1024';
124-
expect(() => calculateRunDynamicMemory(expr, context))
125-
.toThrow(`Invalid variable '{{nonexistentVariable}}' in expression.`);
124+
await expect(calculateRunDynamicMemory(expr, context))
125+
.rejects.toThrow(`Invalid variable '{{nonexistentVariable}}' in expression.`);
126126
});
127127

128-
it('correctly evaluates valid runOptions property', () => {
128+
it('correctly evaluates valid runOptions property', async () => {
129129
const context = { input: {}, runOptions: { memoryMbytes: 16 } };
130130
const expr = '{{runOptions.memoryMbytes}} * 1024';
131-
const result = calculateRunDynamicMemory(expr, context);
131+
const result = await calculateRunDynamicMemory(expr, context);
132132
expect(result).toBe(16384);
133133
});
134134

135-
it('correctly evaluates input property', () => {
135+
it('correctly evaluates input property', async () => {
136136
const context = { input: { value: 16 }, runOptions: { } };
137137
const expr = '{{input.value}} * 1024';
138-
const result = calculateRunDynamicMemory(expr, context);
138+
const result = await calculateRunDynamicMemory(expr, context);
139139
expect(result).toBe(16384);
140140
});
141141

142-
it('should throw error if runOptions property is not supported', () => {
142+
it('should throw error if runOptions property is not supported', async () => {
143143
const context = { input: { value: 16 }, runOptions: { } };
144144
const expr = '{{runOptions.customVariable}} * 1024';
145-
expect(() => calculateRunDynamicMemory(expr, context))
146-
.toThrow(`Invalid variable '{{runOptions.customVariable}}' in expression. Only the following runOptions are allowed:`);
145+
await expect(calculateRunDynamicMemory(expr, context))
146+
.rejects.toThrow(`Invalid variable '{{runOptions.customVariable}}' in expression. Only the following runOptions are allowed:`);
147147
});
148148
});
149149

150150
describe('Rounding logic', () => {
151-
it('should round down (e.g., 10240 -> 8192)', () => {
151+
it('should round down (e.g., 10240 -> 8192)', async () => {
152152
// 2^13 = 8192, 2^14 = 16384.
153-
const result = calculateRunDynamicMemory('10240', emptyContext);
153+
const result = await calculateRunDynamicMemory('10240', emptyContext);
154154
expect(result).toBe(8192);
155155
});
156156

157-
it('should round up (e.g., 13000 -> 16384)', () => {
157+
it('should round up (e.g., 13000 -> 16384)', async () => {
158158
// 13000 is closer to 16384 than 8192.
159-
const result = calculateRunDynamicMemory('13000', emptyContext);
159+
const result = await calculateRunDynamicMemory('13000', emptyContext);
160160
expect(result).toBe(16384);
161161
});
162162

163-
it('should clamp to the minimum memory limit if the result is too low', () => {
164-
const result = calculateRunDynamicMemory('64', emptyContext);
163+
it('should clamp to the minimum memory limit if the result is too low', async () => {
164+
const result = await calculateRunDynamicMemory('64', emptyContext);
165165
expect(result).toBe(128);
166166
});
167167

168-
it('should clamp to the maximum memory limit if the result is too high', () => {
169-
const result = calculateRunDynamicMemory('100000', emptyContext);
168+
it('should clamp to the maximum memory limit if the result is too high', async () => {
169+
const result = await calculateRunDynamicMemory('100000', emptyContext);
170170
expect(result).toBe(32768);
171171
});
172172
});
173173

174174
describe('Invalid/error handling', () => {
175-
it('should throw an error if expression length is greater than DEFAULT_MEMORY_MBYTES_MAX_CHARS', () => {
175+
it('should throw an error if expression length is greater than DEFAULT_MEMORY_MBYTES_MAX_CHARS', async () => {
176176
const expr = '1'.repeat(DEFAULT_MEMORY_MBYTES_EXPRESSION_MAX_LENGTH + 1);
177-
expect(() => calculateRunDynamicMemory(expr, emptyContext))
178-
.toThrow(`The defaultMemoryMbytes expression is too long. Max length is ${DEFAULT_MEMORY_MBYTES_EXPRESSION_MAX_LENGTH} characters.`);
177+
await expect(calculateRunDynamicMemory(expr, emptyContext))
178+
.rejects.toThrow(`The defaultMemoryMbytes expression is too long. Max length is ${DEFAULT_MEMORY_MBYTES_EXPRESSION_MAX_LENGTH} characters.`);
179179
});
180180

181-
it('should throw an error for invalid syntax', () => {
181+
it('should throw an error for invalid syntax', async () => {
182182
const expr = '1 +* 2';
183-
expect(() => calculateRunDynamicMemory(expr, emptyContext))
184-
.toThrow();
183+
await expect(calculateRunDynamicMemory(expr, emptyContext))
184+
.rejects.toThrow();
185185
});
186186

187-
it('should throw error if result is 0', () => {
188-
expect(() => calculateRunDynamicMemory('10 - 10', emptyContext)).toThrow(`Calculated memory value must be a positive number, greater than 0, got: 0.`);
187+
it('should throw error if result is 0', async () => {
188+
await expect(calculateRunDynamicMemory('10 - 10', emptyContext)).rejects.toThrow(`Calculated memory value must be a positive number, greater than 0, got: 0.`);
189189
});
190190

191-
it('should throw error if result is negative', () => {
192-
expect(() => calculateRunDynamicMemory('5 - 10', emptyContext)).toThrow(`Calculated memory value must be a positive number, greater than 0, got: -5.`);
191+
it('should throw error if result is negative', async () => {
192+
await expect(calculateRunDynamicMemory('5 - 10', emptyContext)).rejects.toThrow(`Calculated memory value must be a positive number, greater than 0, got: -5.`);
193193
});
194194

195-
it('should throw error if result is NaN', () => {
196-
expect(() => calculateRunDynamicMemory('0 / 0', emptyContext)).toThrow('Calculated memory value is not a valid number: NaN.');
195+
it('should throw error if result is NaN', async () => {
196+
await expect(calculateRunDynamicMemory('0 / 0', emptyContext)).rejects.toThrow('Calculated memory value is not a valid number: NaN.');
197197
});
198198

199-
it('should throw error if result is a non-numeric (string)', () => {
200-
expect(() => calculateRunDynamicMemory("'hello'", emptyContext)).toThrow('Calculated memory value is not a valid number: hello.');
199+
it('should throw error if result is a non-numeric (string)', async () => {
200+
await expect(calculateRunDynamicMemory("'hello'", emptyContext)).rejects.toThrow('Calculated memory value is not a valid number: hello.');
201201
});
202202

203-
it('should throw error when disabled functionality of MathJS is used', () => {
204-
expect(() => calculateRunDynamicMemory('evaluate(512)', emptyContext)).toThrow('Function evaluate is disabled.');
203+
it('should throw error when disabled functionality of MathJS is used', async () => {
204+
await expect(calculateRunDynamicMemory('evaluate(512)', emptyContext)).rejects.toThrow('Function evaluate is disabled.');
205205
});
206206
});
207207

@@ -213,31 +213,31 @@ describe('calculateDefaultMemoryFromExpression', () => {
213213
beforeEach(() => {
214214
const lruCache = new LruCache<EvalFunction>({ maxLength: 10 });
215215
cache = {
216-
get: (expression: string) => lruCache.get(expression),
217-
set: (expression: string, compilationResult: EvalFunction) => lruCache.add(expression, compilationResult),
218-
length: () => lruCache.length(),
216+
get: async (expression: string) => lruCache.get(expression),
217+
set: async (expression: string, compilationResult: EvalFunction) => { lruCache.add(expression, compilationResult); },
218+
length: async () => lruCache.length(),
219219
};
220220
});
221221

222-
it('correctly works with cache passed in options', () => {
223-
expect(cache.length()).toBe(0);
222+
it('correctly works with cache passed in options', async () => {
223+
expect(await cache.length()).toBe(0);
224224

225225
// First call - cache miss
226-
const result1 = calculateRunDynamicMemory(expr, context, { cache });
226+
const result1 = await calculateRunDynamicMemory(expr, context, { cache });
227227
expect(result1).toBe(8192);
228-
expect(cache.length()).toBe(1); // Expression is now cached
228+
expect(await cache.length()).toBe(1); // Expression is now cached
229229

230230
// Second call - cache hit
231-
const result2 = calculateRunDynamicMemory(expr, context, { cache });
231+
const result2 = await calculateRunDynamicMemory(expr, context, { cache });
232232
expect(result2).toBe(8192);
233-
expect(cache.length()).toBe(1); // Cache length is unchanged
233+
expect(await cache.length()).toBe(1); // Cache length is unchanged
234234
});
235235

236-
it('should cache different expressions separately', () => {
236+
it('should cache different expressions separately', async () => {
237237
const expr2 = 'input.size * 2048'; // 10 * 2048 = 20480 -> 16384
238-
calculateRunDynamicMemory(expr, context, { cache });
239-
calculateRunDynamicMemory(expr2, context, { cache });
240-
expect(cache.length()).toBe(2);
238+
await calculateRunDynamicMemory(expr, context, { cache });
239+
await calculateRunDynamicMemory(expr2, context, { cache });
240+
expect(await cache.length()).toBe(2);
241241
});
242242
});
243243
});

0 commit comments

Comments
 (0)