chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
import {
|
||||
getLargeValueMaterializationError,
|
||||
isLargeValueRef,
|
||||
type LargeValueRef,
|
||||
} from '@/lib/execution/payloads/large-value-ref'
|
||||
|
||||
const FALLBACK_TTL_MS = 15 * 60 * 1000
|
||||
const MAX_IN_MEMORY_BYTES = 256 * 1024 * 1024
|
||||
|
||||
interface LargeValueCacheScope {
|
||||
workspaceId?: string
|
||||
workflowId?: string
|
||||
executionId?: string
|
||||
largeValueExecutionIds?: string[]
|
||||
largeValueKeys?: string[]
|
||||
allowLargeValueWorkflowScope?: boolean
|
||||
}
|
||||
|
||||
const inMemoryValues = new Map<
|
||||
string,
|
||||
{
|
||||
value: unknown
|
||||
size: number
|
||||
expiresAt: number
|
||||
scope?: LargeValueCacheScope
|
||||
recoverable: boolean
|
||||
}
|
||||
>()
|
||||
let inMemoryBytes = 0
|
||||
|
||||
export function clearLargeValueCacheForTests(): void {
|
||||
inMemoryValues.clear()
|
||||
inMemoryBytes = 0
|
||||
}
|
||||
|
||||
function cleanupExpiredValues(now = Date.now()): void {
|
||||
for (const [id, entry] of inMemoryValues.entries()) {
|
||||
if (entry.expiresAt <= now) {
|
||||
inMemoryValues.delete(id)
|
||||
inMemoryBytes -= entry.size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function cacheLargeValue(
|
||||
id: string,
|
||||
value: unknown,
|
||||
size: number,
|
||||
scope?: LargeValueCacheScope,
|
||||
options: { recoverable?: boolean } = {}
|
||||
): boolean {
|
||||
if (size > MAX_IN_MEMORY_BYTES) {
|
||||
return false
|
||||
}
|
||||
|
||||
cleanupExpiredValues()
|
||||
|
||||
const existing = inMemoryValues.get(id)
|
||||
if (existing) {
|
||||
inMemoryValues.delete(id)
|
||||
inMemoryBytes -= existing.size
|
||||
}
|
||||
|
||||
while (inMemoryBytes + size > MAX_IN_MEMORY_BYTES && inMemoryValues.size > 0) {
|
||||
const oldestRecoverableId = Array.from(inMemoryValues.entries()).find(
|
||||
([, entry]) => entry.recoverable
|
||||
)?.[0]
|
||||
if (!oldestRecoverableId) break
|
||||
const oldest = inMemoryValues.get(oldestRecoverableId)
|
||||
inMemoryValues.delete(oldestRecoverableId)
|
||||
inMemoryBytes -= oldest?.size ?? 0
|
||||
}
|
||||
|
||||
if (inMemoryBytes + size > MAX_IN_MEMORY_BYTES) {
|
||||
if (existing) {
|
||||
inMemoryValues.set(id, existing)
|
||||
inMemoryBytes += existing.size
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
inMemoryValues.set(id, {
|
||||
value,
|
||||
size,
|
||||
scope,
|
||||
recoverable: options.recoverable ?? false,
|
||||
expiresAt: Date.now() + FALLBACK_TTL_MS,
|
||||
})
|
||||
inMemoryBytes += size
|
||||
return true
|
||||
}
|
||||
|
||||
function scopeMatchesRef(
|
||||
ref: LargeValueRef,
|
||||
cachedScope: LargeValueCacheScope | undefined,
|
||||
callerScope?: LargeValueCacheScope
|
||||
): boolean {
|
||||
if (!cachedScope?.executionId) {
|
||||
return false
|
||||
}
|
||||
if (ref.executionId && ref.executionId !== cachedScope.executionId) {
|
||||
return false
|
||||
}
|
||||
if (!callerScope) {
|
||||
return Boolean(ref.key) && (!ref.executionId || ref.executionId === cachedScope.executionId)
|
||||
}
|
||||
|
||||
const allowedExecutionIds = new Set([
|
||||
callerScope.executionId,
|
||||
...(callerScope.largeValueExecutionIds ?? []),
|
||||
])
|
||||
if (ref.key && callerScope.largeValueKeys?.includes(ref.key)) {
|
||||
return true
|
||||
}
|
||||
const workflowScopeAllowed =
|
||||
callerScope.allowLargeValueWorkflowScope &&
|
||||
callerScope.workspaceId === cachedScope.workspaceId &&
|
||||
callerScope.workflowId === cachedScope.workflowId
|
||||
|
||||
return allowedExecutionIds.has(cachedScope.executionId) || Boolean(workflowScopeAllowed)
|
||||
}
|
||||
|
||||
export function materializeLargeValueRefSync(
|
||||
ref: LargeValueRef,
|
||||
callerScope?: LargeValueCacheScope
|
||||
): unknown {
|
||||
cleanupExpiredValues()
|
||||
const cached = inMemoryValues.get(ref.id)
|
||||
if (!cached || !scopeMatchesRef(ref, cached.scope, callerScope)) {
|
||||
return undefined
|
||||
}
|
||||
return cached.value
|
||||
}
|
||||
|
||||
export function materializeLargeValueRefSyncOrThrow(
|
||||
ref: LargeValueRef,
|
||||
callerScope?: LargeValueCacheScope
|
||||
): unknown {
|
||||
const materialized = materializeLargeValueRefSync(ref, callerScope)
|
||||
if (materialized === undefined) {
|
||||
throw getLargeValueMaterializationError(ref)
|
||||
}
|
||||
return materialized
|
||||
}
|
||||
|
||||
export function materializeLargeValueRefsSync(
|
||||
value: unknown,
|
||||
seen = new WeakSet<object>()
|
||||
): unknown {
|
||||
if (isLargeValueRef(value)) {
|
||||
return materializeLargeValueRefsSync(materializeLargeValueRefSyncOrThrow(value), seen)
|
||||
}
|
||||
|
||||
if (!value || typeof value !== 'object') {
|
||||
return value
|
||||
}
|
||||
|
||||
if (seen.has(value)) {
|
||||
return value
|
||||
}
|
||||
seen.add(value)
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => materializeLargeValueRefsSync(item, seen))
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).map(([key, entryValue]) => [
|
||||
key,
|
||||
materializeLargeValueRefsSync(entryValue, seen),
|
||||
])
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user