mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 01:57:57 +00:00
Cache mobile typechecks and overlap CI setup (#28192)
* ci: run mobile runtime test groups on separate runners * ci: use a lightweight runner for the mobile result gate * ci: distribute Bun tests across both mobile runners * ci: reuse mobile compiler state without adding runner queues * test: verify eviction of source variants in one cache bucket * test: baseline existing serializer limits in the capacity capture
This commit is contained in:
@@ -13,6 +13,7 @@ describe('compiled operation code', () => {
|
||||
original
|
||||
)
|
||||
expect(compiledOperationModule('different.ts', source, '')).not.toBe(original)
|
||||
expect(compiledOperationModule('original.ts', source, '')).toBe(original)
|
||||
})
|
||||
|
||||
it('bounds retained code and still evaluates an evicted module correctly', () => {
|
||||
@@ -27,4 +28,17 @@ describe('compiled operation code', () => {
|
||||
recompiled(() => undefined, exports)
|
||||
expect(exports).toEqual({ value: 42 })
|
||||
})
|
||||
|
||||
it('bounds retained variants of one file, not only the number of filenames', () => {
|
||||
const source = 'export const value = 42'
|
||||
const original = compiledOperationModule('variants.ts', source, '')
|
||||
for (let index = 0; index < 512; index++) {
|
||||
compiledOperationModule('variants.ts', `${source}; export const variant = ${index}`, '')
|
||||
}
|
||||
const recompiled = compiledOperationModule('variants.ts', source, '')
|
||||
expect(recompiled).not.toBe(original)
|
||||
const exports = {}
|
||||
recompiled(() => undefined, exports)
|
||||
expect(exports).toEqual({ value: 42 })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import { compileFunction } from 'node:vm'
|
||||
import ts from 'typescript'
|
||||
|
||||
const compiled = new Map<string, ReturnType<typeof compileFunction>>()
|
||||
type CompiledModule = {
|
||||
source: string
|
||||
evaluate: ReturnType<typeof compileFunction>
|
||||
}
|
||||
|
||||
const compiled = new Map<string, Map<string, CompiledModule>>()
|
||||
const insertionOrder = new Map<CompiledModule, string>()
|
||||
const MAX_COMPILED_MODULES = 512
|
||||
|
||||
/** Only code is shared: each invocation receives a fresh require closure and exports object. */
|
||||
export function compiledOperationModule(file: string, source: string, exposure: string) {
|
||||
const key = JSON.stringify([file, source, exposure])
|
||||
const cached = compiled.get(key)
|
||||
const key = JSON.stringify([file, exposure])
|
||||
const cached = compiled.get(key)?.get(source)
|
||||
if (cached) {
|
||||
return cached
|
||||
return cached.evaluate
|
||||
}
|
||||
const output = ts.transpileModule(source, {
|
||||
compilerOptions: {
|
||||
@@ -20,12 +26,22 @@ export function compiledOperationModule(file: string, source: string, exposure:
|
||||
}
|
||||
}).outputText
|
||||
const evaluate = compileFunction(output + exposure, ['require', 'exports'], { filename: file })
|
||||
if (compiled.size >= MAX_COMPILED_MODULES) {
|
||||
const oldest = compiled.keys().next().value
|
||||
if (insertionOrder.size >= MAX_COMPILED_MODULES) {
|
||||
const oldest = insertionOrder.entries().next().value
|
||||
if (oldest !== undefined) {
|
||||
compiled.delete(oldest)
|
||||
const [entry, variantKey] = oldest
|
||||
insertionOrder.delete(entry)
|
||||
const entries = compiled.get(variantKey)
|
||||
entries?.delete(entry.source)
|
||||
if (!entries?.size) {
|
||||
compiled.delete(variantKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
compiled.set(key, evaluate)
|
||||
const entry = { source, evaluate }
|
||||
const entries = compiled.get(key) ?? new Map<string, CompiledModule>()
|
||||
entries.set(source, entry)
|
||||
compiled.set(key, entries)
|
||||
insertionOrder.set(entry, key)
|
||||
return evaluate
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { expect, it, vi } from 'vitest'
|
||||
import { internRecording, resolveRecording } from './golden-value-pool'
|
||||
|
||||
it('serializes each pooled value once for its hash and collision check', () => {
|
||||
const recording = {
|
||||
scenario: 'serialization',
|
||||
checkpoints: [
|
||||
{
|
||||
id: 'one',
|
||||
observation: {
|
||||
sender: [],
|
||||
payloads: [],
|
||||
settlements: {},
|
||||
state: { value: 'one' },
|
||||
effects: []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
const stringify = vi.spyOn(JSON, 'stringify')
|
||||
try {
|
||||
const interned = internRecording(recording)
|
||||
const calls = stringify.mock.calls.length
|
||||
stringify.mockRestore()
|
||||
expect(calls).toBe(2)
|
||||
expect(resolveRecording(interned.values, interned.recording)).toEqual(recording)
|
||||
} finally {
|
||||
stringify.mockRestore()
|
||||
}
|
||||
})
|
||||
@@ -61,7 +61,11 @@ export function canonicalJson(value: RecordedValue): string {
|
||||
}
|
||||
|
||||
export function valueHash(value: RecordedValue): string {
|
||||
return createHash('sha256').update(canonicalJson(value)).digest('hex').slice(0, 12)
|
||||
return canonicalJsonHash(canonicalJson(value))
|
||||
}
|
||||
|
||||
function canonicalJsonHash(json: string): string {
|
||||
return createHash('sha256').update(json).digest('hex').slice(0, 12)
|
||||
}
|
||||
|
||||
function listEntries(at: string, value: RecordedValue): RecordedValue[] {
|
||||
@@ -86,8 +90,8 @@ export function internRecording(recording: Recording): {
|
||||
const pool: ValuePool = {}
|
||||
const canonical = new Map<string, string>()
|
||||
const intern = (entry: RecordedValue, at: string): string => {
|
||||
const hash = valueHash(entry)
|
||||
const json = canonicalJson(entry)
|
||||
const hash = canonicalJsonHash(json)
|
||||
const seen = canonical.get(hash)
|
||||
if (seen !== undefined && seen !== json) {
|
||||
throw new Error(`Golden value hash collision at ${hash} (${at})`)
|
||||
|
||||
Reference in New Issue
Block a user