forked from Vatix-Protocol/vatix-backend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoracle-service.ts
More file actions
219 lines (199 loc) · 5.97 KB
/
Copy pathoracle-service.ts
File metadata and controls
219 lines (199 loc) · 5.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/**
* Oracle Service
*
* Orchestrates market resolution by coordinating primary and fallback providers.
* Switches to fallback on primary failure and logs/metrics fallback usage.
*
* @module apps/oracle/oracle-service
*/
import type {
ProviderAdapter,
ProviderResult,
ResolutionRequest,
} from "./provider-adapter.js";
import { withTimeout, DEFAULT_TIMEOUT_MS } from "./timeout-utils.js";
import { withRetry, RetryConfig } from "./retry-utils.js";
/**
* Oracle service configuration.
*/
export interface OracleServiceConfig {
/** Primary provider adapter */
primaryAdapter: ProviderAdapter;
/** Fallback provider adapter */
fallbackAdapter: ProviderAdapter;
/** Whether to enable fallback on primary failure */
enableFallback?: boolean;
/** Default timeout for resolution requests */
defaultTimeoutMs?: number;
/** Retry configuration for provider calls */
retryConfig?: Partial<RetryConfig>;
}
/**
* Metrics for tracking provider usage.
*/
export interface OracleMetrics {
/** Number of successful primary resolutions */
primarySuccessCount: number;
/** Number of primary failures */
primaryFailureCount: number;
/** Number of fallback resolutions used */
fallbackUsageCount: number;
/** Number of fallback failures */
fallbackFailureCount: number;
/** Total resolution attempts */
totalAttempts: number;
/** Total retry attempts across all primary resolutions */
retryCount: number;
}
/**
* Oracle service for market resolution.
* Uses primary adapter by default, switches to fallback on primary failure.
*/
export class OracleService {
private primaryAdapter: ProviderAdapter;
private fallbackAdapter: ProviderAdapter;
private config: OracleServiceConfig;
private metrics: OracleMetrics = {
primarySuccessCount: 0,
primaryFailureCount: 0,
fallbackUsageCount: 0,
fallbackFailureCount: 0,
totalAttempts: 0,
retryCount: 0,
};
constructor(config: OracleServiceConfig) {
this.primaryAdapter = config.primaryAdapter;
this.fallbackAdapter = config.fallbackAdapter;
this.config = {
enableFallback: true,
defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
...config,
};
}
/**
* Resolve a market using the primary provider.
* Falls back to the secondary provider if the primary fails.
*
* @param request - Resolution request parameters
* @returns Provider result with source attribution
* @throws Error if both primary and fallback fail
*/
async resolve(request: ResolutionRequest): Promise<ProviderResult> {
this.metrics.totalAttempts++;
try {
// Attempt primary provider
console.log(
`[OracleService] Resolving market ${request.marketId} using primary provider`
);
const result = await withRetry(
() => this.primaryAdapter.resolve(request),
this.config.retryConfig,
(error, attempt, delay) => {
this.metrics.retryCount++;
console.warn(
`[OracleService] Primary provider retry ${attempt} for market ${request.marketId} (delay: ${delay.toFixed(0)}ms): ${error.message}`
);
}
);
const result = await this.primaryAdapter.resolve(request);
this.metrics.primarySuccessCount++;
console.log(
`[OracleService] Primary provider succeeded for market ${request.marketId} (source: ${result.source})`
);
return result;
} catch (primaryError) {
this.metrics.primaryFailureCount++;
console.error(
`[OracleService] Primary provider failed for market ${request.marketId}:`,
primaryError instanceof Error ? primaryError.message : primaryError
);
// If fallback is disabled, re-throw the error
if (!this.config.enableFallback) {
throw primaryError;
}
// Attempt fallback provider
return this.resolveWithFallback(request);
}
}
/**
* Resolve a market using the fallback provider.
* Logs and metrics fallback usage.
*
* @param request - Resolution request parameters
* @returns Provider result with source attribution
* @throws Error if fallback also fails
*/
private async resolveWithFallback(
request: ResolutionRequest
): Promise<ProviderResult> {
console.warn(
`[OracleService] Falling back to secondary provider for market ${request.marketId}`
);
try {
const result = await this.fallbackAdapter.resolve(request);
this.metrics.fallbackUsageCount++;
console.log(
`[OracleService] Fallback provider succeeded for market ${request.marketId} (source: ${result.source})`
);
return result;
} catch (fallbackError) {
this.metrics.fallbackFailureCount++;
console.error(
`[OracleService] Fallback provider also failed for market ${request.marketId}:`,
fallbackError instanceof Error ? fallbackError.message : fallbackError
);
throw new Error(
`All providers failed for market ${request.marketId}. Primary: ${
fallbackError instanceof Error
? fallbackError.message
: String(fallbackError)
}`
);
}
}
/**
* Check if the primary provider is healthy.
*
* @returns True if the primary provider is healthy
*/
async healthCheck(): Promise<boolean> {
try {
return await this.primaryAdapter.healthCheck();
} catch {
return false;
}
}
/**
* Get current oracle metrics.
*
* @returns OracleMetrics snapshot
*/
getMetrics(): OracleMetrics {
return { ...this.metrics };
}
/**
* Reset oracle metrics.
*/
resetMetrics(): void {
this.metrics = {
primarySuccessCount: 0,
primaryFailureCount: 0,
fallbackUsageCount: 0,
fallbackFailureCount: 0,
totalAttempts: 0,
retryCount: 0,
};
}
/**
* Get the primary adapter instance.
*/
getPrimaryAdapter(): ProviderAdapter {
return this.primaryAdapter;
}
/**
* Get the fallback adapter instance.
*/
getFallbackAdapter(): ProviderAdapter {
return this.fallbackAdapter;
}
}