Skip to content

Commit bc059c1

Browse files
authored
Merge pull request #283 from bbopen/fix/0.9-remove-result-cache
fix(runtime)!: remove heuristic call-result caching (enableCache)
2 parents d1d636f + 3d364df commit bc059c1

6 files changed

Lines changed: 3 additions & 120 deletions

File tree

docs/guide/runtimes/node.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ const bridge = new NodeBridge({
160160
| `maxProcesses` | `number` | `1` | Maximum worker count |
161161
| `maxConcurrentPerProcess` | `number` | `10` | Concurrent requests per worker |
162162
| `inheritProcessEnv` | `boolean` | `false` | Pass the full parent environment through |
163-
| `enableCache` | `boolean` | `false` | Cache pure function results |
164163
| `env` | `Record<string, string \| undefined>` | `{}` | Extra subprocess env vars |
165164
| `codec` | `CodecOptions` || Codec validation and byte handling |
166165
| `warmupCommands` | `Array<{ module, functionName, args? }>` | `[]` | Commands to run when each worker starts |

docs/public/llms-full.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,7 +1254,6 @@ const bridge = new NodeBridge({
12541254
| `maxProcesses` | `number` | `1` | Maximum worker count |
12551255
| `maxConcurrentPerProcess` | `number` | `10` | Concurrent requests per worker |
12561256
| `inheritProcessEnv` | `boolean` | `false` | Pass the full parent environment through |
1257-
| `enableCache` | `boolean` | `false` | Cache pure function results |
12581257
| `env` | `Record<string, string \| undefined>` | `{}` | Extra subprocess env vars |
12591258
| `codec` | `CodecOptions` | — | Codec validation and byte handling |
12601259
| `warmupCommands` | `Array<{ module, functionName, args? }>` | `[]` | Commands to run when each worker starts |
@@ -3219,7 +3218,6 @@ const info = await bridge.getBridgeInfo({ refresh: true });
32193218
| `maxProcesses` | `1` | Maximum worker count |
32203219
| `maxConcurrentPerProcess` | `10` | Concurrent requests per worker |
32213220
| `inheritProcessEnv` | `false` | Pass full parent env through |
3222-
| `enableCache` | `false` | Cache pure function results |
32233221
| `env` | `{}` | Extra subprocess env vars |
32243222
| `codec` | — | `CodecOptions` for validation and byte handling |
32253223
| `warmupCommands` | `[]` | Per-worker startup calls |

docs/reference/api/index.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ const info = await bridge.getBridgeInfo({ refresh: true });
142142
| `maxProcesses` | `1` | Maximum worker count |
143143
| `maxConcurrentPerProcess` | `10` | Concurrent requests per worker |
144144
| `inheritProcessEnv` | `false` | Pass full parent env through |
145-
| `enableCache` | `false` | Cache pure function results |
146145
| `env` | `{}` | Extra subprocess env vars |
147146
| `codec` || `CodecOptions` for validation and byte handling |
148147
| `warmupCommands` | `[]` | Per-worker startup calls |

src/runtime/base-bridge.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
*
44
* The three bridge facades (NodeBridge/HttpBridge/PyodideBridge) all extend
55
* DisposableBase (lifecycle/resources) and implement PythonRuntime by HOLDING
6-
* an RpcClient. The PythonRuntime delegation was byte-identical across all
6+
* an RpcClient. The PythonRuntime delegation is byte-identical across all
77
* three: each method does `await this.ensureReady()` then forwards to the held
88
* RpcClient. This base collapses that duplication onto a single
99
* `getRpcClient()` accessor while leaving each facade free to own its own
10-
* RpcClient field (for constructor wiring, resource tracking, and — in
11-
* NodeBridge's case — a caching override of call()).
10+
* RpcClient field for constructor wiring and resource tracking.
1211
*
1312
* It carries no transport/codec/lifecycle specifics: doInit/doDispose remain
1413
* abstract on DisposableBase and stay per-facade.

src/runtime/node.ts

Lines changed: 1 addition & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { createRequire } from 'node:module';
1616
import { autoRegisterArrowDecoder } from '../utils/codec.js';
1717
import { getDefaultPythonPath } from '../utils/python.js';
1818
import { getVenvBinDir, getVenvPythonExe } from '../utils/runtime.js';
19-
import { globalCache } from '../utils/cache.js';
2019

2120
import { BasePythonBridge } from './base-bridge.js';
2221
import { RpcClient } from './rpc-client.js';
@@ -64,9 +63,6 @@ export interface NodeBridgeOptions {
6463
/** Inherit all environment variables from parent process. Default: false */
6564
inheritProcessEnv?: boolean;
6665

67-
/** Enable result caching for pure functions. Default: false */
68-
enableCache?: boolean;
69-
7066
/** Optional extra environment variables to pass to the Python subprocess. */
7167
env?: Record<string, string | undefined>;
7268

@@ -132,7 +128,6 @@ interface ResolvedOptions {
132128
timeoutMs: number;
133129
queueTimeoutMs: number;
134130
inheritProcessEnv: boolean;
135-
enableCache: boolean;
136131
enableChunking: boolean;
137132
env: Record<string, string | undefined>;
138133
codec?: CodecOptions;
@@ -293,7 +288,6 @@ function normalizeWarmupCommands(commands: NodeBridgeOptions['warmupCommands']):
293288
* - Virtual environment support
294289
* - Full BridgeCodec validation (NaN/Infinity rejection, key validation)
295290
* - Automatic Arrow decoding for DataFrames/ndarrays
296-
* - Optional result caching for pure functions
297291
* - Process warmup commands
298292
*
299293
* @example
@@ -314,7 +308,6 @@ function normalizeWarmupCommands(commands: NodeBridgeOptions['warmupCommands']):
314308
* const pooledBridge = new NodeBridge({
315309
* maxProcesses: 4,
316310
* maxConcurrentPerProcess: 2,
317-
* enableCache: true,
318311
* });
319312
* await pooledBridge.init();
320313
* ```
@@ -352,7 +345,6 @@ export class NodeBridge extends BasePythonBridge {
352345
timeoutMs: options.timeoutMs ?? 30000,
353346
queueTimeoutMs: options.queueTimeoutMs ?? 30000,
354347
inheritProcessEnv: options.inheritProcessEnv ?? false,
355-
enableCache: options.enableCache ?? false,
356348
enableChunking: options.enableChunking ?? true,
357349
env: options.env ?? {},
358350
codec: options.codec,
@@ -457,57 +449,12 @@ export class NodeBridge extends BasePythonBridge {
457449

458450
/**
459451
* Expose the held RpcClient to BasePythonBridge's shared delegating methods
460-
* (instantiate/callMethod/disposeInstance/getBridgeInfo). call() is
461-
* overridden below to layer caching on top.
452+
* (call/instantiate/callMethod/disposeInstance/getBridgeInfo).
462453
*/
463454
protected getRpcClient(): RpcClient {
464455
return this.rpc;
465456
}
466457

467-
/**
468-
* Call a Python function, with optional result caching.
469-
*
470-
* Overrides BasePythonBridge.call() to layer the cache lookup/writeback on
471-
* top of the shared delegation. Cache lookup stays FIRST so cache hits return
472-
* without forcing init, preserving the pre-composition behavior.
473-
*/
474-
override async call<T = unknown>(
475-
module: string,
476-
functionName: string,
477-
args: unknown[],
478-
kwargs?: Record<string, unknown>
479-
): Promise<T> {
480-
// Check cache if enabled
481-
if (this.resolvedOptions.enableCache) {
482-
const cacheKey = this.safeCacheKey('runtime_call', module, functionName, args, kwargs);
483-
if (cacheKey) {
484-
const cached = await globalCache.get<T>(cacheKey);
485-
if (cached !== null) {
486-
return cached;
487-
}
488-
}
489-
490-
// Execute and cache if pure function
491-
const startTime = performance.now();
492-
await this.ensureReady();
493-
const result = await this.rpc.call<T>(module, functionName, args, kwargs);
494-
const duration = performance.now() - startTime;
495-
496-
if (cacheKey && this.isPureFunctionCandidate(functionName, args)) {
497-
await globalCache.set(cacheKey, result, {
498-
computeTime: duration,
499-
dependencies: [module],
500-
});
501-
}
502-
503-
return result;
504-
}
505-
506-
// No caching - direct call
507-
await this.ensureReady();
508-
return this.rpc.call<T>(module, functionName, args, kwargs);
509-
}
510-
511458
// ===========================================================================
512459
// POOL STATISTICS
513460
// ===========================================================================
@@ -561,52 +508,6 @@ export class NodeBridge extends BasePythonBridge {
561508
busyWorkers: poolStats.totalInFlight,
562509
};
563510
}
564-
565-
// ===========================================================================
566-
// PRIVATE HELPERS
567-
// ===========================================================================
568-
569-
/**
570-
* Generate a cache key, returning null if generation fails.
571-
*/
572-
private safeCacheKey(prefix: string, ...inputs: unknown[]): string | null {
573-
try {
574-
return globalCache.generateKey(prefix, ...inputs);
575-
} catch {
576-
return null;
577-
}
578-
}
579-
580-
/**
581-
* Heuristic to determine if function result should be cached.
582-
*/
583-
private isPureFunctionCandidate(functionName: string, args: unknown[]): boolean {
584-
const pureFunctionPatterns = [
585-
/^(get|fetch|read|load|find|search|query|select)_/i,
586-
/^(compute|calculate|process|transform|convert)_/i,
587-
/^(encode|decode|serialize|deserialize)_/i,
588-
];
589-
590-
const impureFunctionPatterns = [
591-
/^(set|save|write|update|insert|delete|create|modify)_/i,
592-
/^(send|post|put|patch)_/i,
593-
/random|uuid|timestamp|now|current/i,
594-
];
595-
596-
if (impureFunctionPatterns.some(pattern => pattern.test(functionName))) {
597-
return false;
598-
}
599-
600-
if (pureFunctionPatterns.some(pattern => pattern.test(functionName))) {
601-
return true;
602-
}
603-
604-
const hasComplexArgs = args.some(
605-
arg => arg !== null && typeof arg === 'object' && !(arg instanceof Date)
606-
);
607-
608-
return !hasComplexArgs && args.length <= 3;
609-
}
610511
}
611512

612513
// =============================================================================

test/runtime_config.test.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -491,19 +491,6 @@ describe('Runtime Configuration', () => {
491491
});
492492
});
493493

494-
it('should configure caching options', () => {
495-
const cacheConfig = {
496-
enableCache: true,
497-
maxCacheSize: 1024 * 1024, // 1MB
498-
cacheTTL: 300000, // 5 minutes
499-
cacheStrategy: 'lru',
500-
};
501-
502-
expect(cacheConfig.enableCache).toBe(true);
503-
expect(cacheConfig.maxCacheSize).toBe(1048576);
504-
expect(cacheConfig.cacheStrategy).toBe('lru');
505-
});
506-
507494
it('should configure memory limits', () => {
508495
const memoryConfig = {
509496
maxHeapSize: 512 * 1024 * 1024, // 512MB

0 commit comments

Comments
 (0)