Skip to content

Commit f64d848

Browse files
committed
Update error handling for strategy execution
1 parent 19091ba commit f64d848

File tree

4 files changed

+66
-28
lines changed

4 files changed

+66
-28
lines changed

packages/transaction-decoder/src/abi-loader.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const getMany = (requests: Array<AbiStore.AbiParams>) =>
6161
return yield* Effect.all(
6262
requests.map(({ chainID, address, event, signature }) => get({ chainID, address, event, signature })),
6363
{
64-
concurrency: 'inherit',
64+
concurrency: 'unbounded',
6565
batching: 'inherit',
6666
},
6767
)
@@ -170,6 +170,7 @@ export const AbiLoaderRequestResolver = RequestResolver.makeBatched((requests: A
170170
},
171171
{
172172
discard: true,
173+
concurrency: 'unbounded',
173174
},
174175
)
175176

@@ -182,6 +183,7 @@ export const AbiLoaderRequestResolver = RequestResolver.makeBatched((requests: A
182183
}
183184
}
184185

186+
const concurrency = Math.min(...[...concurrencyMap.values(), 50]) // Use minimum concurrency across all chains, capped at 25
185187
// NOTE: Firstly we batch strategies by address because in a transaction most of events and traces are from the same abi
186188
const response = yield* Effect.forEach(
187189
remaining,
@@ -196,10 +198,14 @@ export const AbiLoaderRequestResolver = RequestResolver.makeBatched((requests: A
196198
chainId: req.chainID,
197199
strategyId: 'address-batch',
198200
})
199-
.pipe(Effect.map((result) => (result ? Either.left(result) : Either.right(req))))
201+
.pipe(
202+
Effect.tapError(Effect.logWarning),
203+
Effect.orElseSucceed(() => null),
204+
Effect.map((result) => (result ? Either.left(result) : Either.right(req))),
205+
)
200206
},
201207
{
202-
concurrency: Math.min(...[...concurrencyMap.values()], 50), // Use minimum to be conservative
208+
concurrency,
203209
},
204210
)
205211

@@ -213,16 +219,21 @@ export const AbiLoaderRequestResolver = RequestResolver.makeBatched((requests: A
213219
(strategy) => strategy.type === 'fragment',
214220
)
215221

216-
return strategyExecutor.executeStrategiesSequentially(allAvailableStrategies, {
217-
address,
218-
chainId: chainID,
219-
event,
220-
signature,
221-
strategyId: 'fragment-batch',
222-
})
222+
return strategyExecutor
223+
.executeStrategiesSequentially(allAvailableStrategies, {
224+
address,
225+
chainId: chainID,
226+
event,
227+
signature,
228+
strategyId: 'fragment-batch',
229+
})
230+
.pipe(
231+
Effect.tapError(Effect.logWarning),
232+
Effect.orElseSucceed(() => null),
233+
) // If no strategies found, return null
223234
},
224235
{
225-
concurrency: Math.min(...[...concurrencyMap.values()], 25), // More conservative for fragments
236+
concurrency,
226237
batching: true,
227238
},
228239
)

packages/transaction-decoder/src/abi-strategy/strategy-executor.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
import { Effect, Schedule, Duration, pipe } from 'effect'
1+
import { Effect, Schedule, Duration, pipe, Data } from 'effect'
22
import { GetContractABIStrategyParams, ContractAbiResolverStrategy } from '../abi-strategy/request-model.js'
33
import type { CircuitBreaker } from '../circuit-breaker/circuit-breaker.js'
44
import { RequestPool } from '../circuit-breaker/request-pool.js'
55
import * as Constants from '../circuit-breaker/constants.js'
66

7+
export class MissingHealthyStrategy extends Data.TaggedError('MissingHealthyStrategy')<{
8+
chainId: number
9+
strategies: string[]
10+
}> {
11+
constructor(params: { chainId: number; strategies: string[] }) {
12+
super(params)
13+
}
14+
}
15+
716
export const make = (circuitBreaker: CircuitBreaker, requestPool: RequestPool) => {
817
const executeStrategy = (strategy: ContractAbiResolverStrategy, params: GetContractABIStrategyParams) => {
918
return pipe(
@@ -44,14 +53,16 @@ export const make = (circuitBreaker: CircuitBreaker, requestPool: RequestPool) =
4453
}
4554

4655
if (healthyStrategies.length === 0) {
47-
yield* Effect.logWarning(`No healthy strategies available for chain ${params.chainId}`)
48-
return null
56+
return yield* Effect.fail(
57+
new MissingHealthyStrategy({
58+
chainId: params.chainId,
59+
strategies: strategies.map((s) => s.id),
60+
}),
61+
)
4962
}
5063

5164
// Try strategies one by one until one succeeds
52-
return yield* Effect.validateFirst(healthyStrategies, (strategy) => executeStrategy(strategy, params)).pipe(
53-
Effect.orElseSucceed(() => null),
54-
)
65+
return yield* Effect.validateFirst(healthyStrategies, (strategy) => executeStrategy(strategy, params))
5566
})
5667

5768
return {

packages/transaction-decoder/src/contract-meta-loader.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,16 @@ const ContractMetaLoaderRequestResolver = RequestResolver.makeBatched((requests:
138138
({ chainID, address }) => {
139139
const allAvailableStrategies = Array.prependAll(strategies.default, strategies[chainID] ?? [])
140140

141-
return metaStrategyExecutor.executeStrategiesSequentially(allAvailableStrategies, {
142-
address,
143-
chainId: chainID,
144-
strategyId: 'meta-batch',
145-
})
141+
return metaStrategyExecutor
142+
.executeStrategiesSequentially(allAvailableStrategies, {
143+
address,
144+
chainId: chainID,
145+
strategyId: 'meta-batch',
146+
})
147+
.pipe(
148+
Effect.tapError(Effect.logWarning),
149+
Effect.orElseSucceed(() => null),
150+
)
146151
},
147152
{
148153
concurrency: Math.min(...[...concurrencyMap.values()], 25), // Conservative concurrency

packages/transaction-decoder/src/meta-strategy/strategy-executor.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
import { Effect, Schedule, Duration, pipe } from 'effect'
1+
import { Effect, Schedule, Duration, pipe, Data } from 'effect'
22
import { FetchMetaParams, ContractMetaResolverStrategy, GetContractMetaStrategy } from './request-model.js'
33
import type { CircuitBreaker } from '../circuit-breaker/circuit-breaker.js'
44
import { RequestPool } from '../circuit-breaker/request-pool.js'
55
import * as Constants from '../circuit-breaker/constants.js'
66

7+
export class MissingHealthyStrategy extends Data.TaggedError('MissingHealthyStrategy')<{
8+
chainId: number
9+
strategies: string[]
10+
}> {
11+
constructor(params: { chainId: number; strategies: string[] }) {
12+
super(params)
13+
}
14+
}
15+
716
export const make = (circuitBreaker: CircuitBreaker, requestPool: RequestPool) => {
817
const executeStrategy = (strategy: ContractMetaResolverStrategy, params: FetchMetaParams) => {
918
return pipe(
@@ -49,14 +58,16 @@ export const make = (circuitBreaker: CircuitBreaker, requestPool: RequestPool) =
4958
}
5059

5160
if (healthyStrategies.length === 0) {
52-
yield* Effect.logWarning(`No healthy meta strategies available for chain ${params.chainId}`)
53-
return null
61+
return yield* Effect.fail(
62+
new MissingHealthyStrategy({
63+
chainId: params.chainId,
64+
strategies: strategies.map((s) => s.id),
65+
}),
66+
)
5467
}
5568

5669
// Try strategies one by one until one succeeds
57-
return yield* Effect.validateFirst(healthyStrategies, (strategy) => executeStrategy(strategy, params)).pipe(
58-
Effect.orElseSucceed(() => null),
59-
)
70+
return yield* Effect.validateFirst(healthyStrategies, (strategy) => executeStrategy(strategy, params))
6071
})
6172

6273
return {

0 commit comments

Comments
 (0)