-
Notifications
You must be signed in to change notification settings - Fork 331
Open
Description
Problem
PythLazerClient.create() blocks indefinitely when WebSocket endpoints are unreachable. The WebSocketPool.create() method contains this loop:
while(!pool.isAnyConnectionEstablished()) {
await new Promise((resolve) => setTimeout(resolve, 100));
}This causes issues for fault-tolerant services (e.g., relayers) that need to:
- Handle Pyth Pro being temporarily unavailable
- Implement graceful degradation
- Avoid hanging promises that block service startup
Proposed Solution
Add optional AbortSignal and connectionTimeoutMs parameters to WebSocketPoolConfig:
export type WebSocketPoolConfig = {
// ... existing fields ...
/**
* AbortSignal to cancel connection establishment.
* If aborted before connection is established, create() will throw.
*/
signal?: AbortSignal;
/**
* Timeout in ms for initial connection establishment.
* If not connected within this time, create() will throw.
* @defaultValue Infinity (no timeout)
*/
connectionTimeoutMs?: number;
};Implementation
In WebSocketPool.create():
static async create(config: WebSocketPoolConfig, token: string, logger?: Logger): Promise<WebSocketPool> {
// ... existing setup code ...
const startTime = Date.now();
const timeoutMs = config.connectionTimeoutMs ?? Infinity;
while(!pool.isAnyConnectionEstablished()) {
// Check abort signal
if (config.signal?.aborted) {
pool.shutdown();
throw new DOMException('Connection aborted', 'AbortError');
}
// Check timeout
if (Date.now() - startTime > timeoutMs) {
pool.shutdown();
throw new Error(`Connection timeout: failed to establish connection within ${timeoutMs}ms`);
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
// ... rest of method ...
}Usage Examples
With AbortSignal
const controller = new AbortController();
// Abort after 5 seconds
setTimeout(() => controller.abort(), 5000);
try {
const client = await PythLazerClient.create({
token: process.env.ACCESS_TOKEN!,
webSocketPoolConfig: {
urls: [...],
signal: controller.signal
}
});
} catch (err) {
if (err.name === 'AbortError') {
console.log('Connection was aborted');
// Handle gracefully - e.g., continue without real-time prices
}
}With timeout (simpler API)
try {
const client = await PythLazerClient.create({
token: process.env.ACCESS_TOKEN!,
webSocketPoolConfig: {
urls: [...],
connectionTimeoutMs: 5000
}
});
} catch (err) {
console.log('Connection timed out, falling back to REST API');
}Current workaround
// Users currently have to do this manually:
const timeout = (ms) => new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), ms)
);
const client = await Promise.race([
PythLazerClient.create({ token, webSocketPoolConfig: { urls } }),
timeout(5000)
]);
// Problem: WebSocket connections keep running in background after timeoutUse Case
A relayer service that needs high fault tolerance reported this issue. When Pyth Pro endpoints are temporarily unavailable, their service hangs on startup instead of gracefully degrading to alternative data sources.
This is a standard pattern for async operations in modern JavaScript/TypeScript — fetch(), ReadableStream, and many other APIs support AbortSignal for cancellation.
Additional Context
- SDK version: 6.0.0
- The
AbortSignalpattern aligns with web platform standards - Both options (
signalandconnectionTimeoutMs) provide flexibility for different use cases
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels