You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: lazy per-chain provider/validator construction to fix PC OOM (#665)
* fix: lazy per-chain provider/validator construction to fix PC OOM
PostOrder/PostLimitOrder/CheckOrderStatus were constructing one
StaticJsonRpcProvider plus one or more OnChain*Validator instances per
SUPPORTED_CHAIN at module load. After SUPPORTED_CHAINS grew from 7 to 18
in #654, provisioned-concurrency pre-warm OOMed again ("Provisioned
Concurrency configuration failed to be applied. Reason: FAILED"),
undoing the headroom that the provider-dedup fix in #663 had just
carved out.
Decouple cold-start memory from chain count by constructing providers
and validators on first per-chain access instead of eagerly at module
load:
- OnChainValidatorMap accepts an optional { factory, isSupported }
options object. get() lazily constructs and caches via the factory;
has() consults isSupported. Existing positional initial-array
constructor and set() API are preserved so tests and call sites that
pass a pre-built mapping are unchanged.
- ProviderMap is loosened from Map<ChainId, StaticJsonRpcProvider> to a
structural interface { get(chainId): StaticJsonRpcProvider | undefined };
Map still satisfies it. New LazyProviderMap class constructs one
provider per chain on first access and caches it.
- post-order, post-limit-order, and check-order-status drop their
SUPPORTED_CHAINS module-load loops in favor of lazy maps. Cold start
now allocates zero providers and zero validators; first request to a
chain pays a one-time alloc.
Also fixes a latent bug in post-limit-order/index.ts where the second
SUPPORTED_CHAINS loop was populating onChainValidatorMap a second time
instead of relayOrderValidatorMap, leaving relay orders on the limit
route without an on-chain validator. The factory rewrite removes the
duplicated loop entirely.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: replace ! assertions in factories with LazyProviderMap.getOrThrow
The factory closures all used providerMap.get(chainId)! to coerce the
optional return into a non-null provider. That's safe today because every
validator map's isSupported predicate is a subset of LazyProviderMap's
supported set, but it's a brittle invariant — narrowing the supported set
in a future change would silently produce undefined providers wrapped
inside validator instances.
Add LazyProviderMap.getOrThrow(chainId): StaticJsonRpcProvider that throws
a clear error if the chainId isn't supported, and use it from the four
factory closures. The structural ProviderMap interface (Map-compatible
.get returning T | undefined) is unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(lambda): bump prod PostOrder/PostLimitOrder memorySize 1024 -> 2048 MB
Defense-in-depth on top of the lazy provider/validator init in this PR.
Lazy construction eliminates the cold-start spike, but prod runs ~50x
the traffic of beta and will reach steady-state with all 18
SUPPORTED_CHAINS warmed in memory faster than beta. Doubling memory
keeps PC pre-warm and steady-state allocations well clear of the OOM
boundary that's bitten us once already at 18 chains.
Beta stays at 1024 MB so it remains a fair canary for whether the lazy
fix is sufficient on its own at future chain counts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: drop isSupported gate; trust boundary SUPPORTED_CHAINS validation
The HTTP request boundary already enforces chainId ∈ SUPPORTED_CHAINS
via Joi (lib/util/field-validator.ts:59, CHAIN_ID_JOI). Step Functions
events in check-order-status are seeded by post-order, which inherits
that validation. With the boundary trusted, the duplicated isSupported
predicates in OnChainValidatorMap factories and the supported set in
LazyProviderMap were dead weight.
- OnChainValidatorMap: drop OnChainValidatorMapOptions and the has()
method. Factory now passed positionally; get() always calls it on
cache miss. has() had a single caller (V4 quoter check in
UniswapXOrderService) handled below.
- LazyProviderMap: drop the supported constructor param and getOrThrow().
get() now constructs+caches unconditionally and returns a
StaticJsonRpcProvider non-null. The ProviderMap structural interface
still allows undefined for Map<> compatibility.
- UniswapXOrderService: replace this.onChainV4ValidatorMap?.has(chainId)
with a direct UNISWAPX_V4_ORDER_QUOTER_MAPPING[chainId] lookup. The V4
quoter mapping is a property of the SDK + per-chain reactor deploys,
not of the validator-map abstraction — keeping it at the call site
removes the abstraction's only V4-specific concern.
- Handlers: drop the supportedChainSet/isSupportedChain boilerplate;
factory closures pass providerMap.get(chainId) directly.
Net: -37 lines, no remaining duplicate "is this chain supported" logic.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0 commit comments