Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 33 additions & 11 deletions packages/socket-server/src/socket/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { AxiosInstance } from 'axios'

import { TransactionEventHandler } from '@modules/transactions'
import { SwitchNetworkEventHandler } from '../modules/switchNetwork'
import { DatabaseClass } from '@utils/database'

import { SocketEvents, SocketUsernames } from '../types'
import { DatabaseClass, retryWithBackoff } from '@src/utils'

// import Redis from 'ioredis'
// import { createAdapter } from '@socket.io/redis-adapter'
Expand Down Expand Up @@ -79,16 +80,37 @@ export const setupSocket = (io: SocketIOServer, database: DatabaseClass, api: Ax
socket.on(SocketEvents.CONNECTION_STATE, async () => {
const { sessionId, request_id } = socket.handshake.auth
const connectorRoom = `${sessionId}:${SocketUsernames.CONNECTOR}:${request_id}`
const { data: connected } = await api.get(`/connections/${sessionId}/state`)

io.to(connectorRoom).emit(SocketEvents.CONNECTION_STATE, {
username: SocketUsernames.CONNECTOR,
request_id,
room: sessionId,
to: SocketUsernames.CONNECTOR,
type: SocketEvents.CONNECTION_STATE,
data: connected,
})

try {
const connectionStateUrl = `/connections/${sessionId}/state`
const { data: connected } = await retryWithBackoff(() => api.get(connectionStateUrl), connectionStateUrl)

console.log('[SOCKET] [CONNECTION_STATE] Connected state for session', sessionId, '->', connected)

io.to(connectorRoom).emit(SocketEvents.CONNECTION_STATE, {
username: SocketUsernames.CONNECTOR,
request_id,
room: sessionId,
to: SocketUsernames.CONNECTOR,
type: SocketEvents.CONNECTION_STATE,
data: connected,
})
} catch (error) {
console.error('[SOCKET] [CONNECTION_STATE] Error:', {
message: error.message,
status: error.response?.status,
url: error.config?.url,
})
// Returns an error response to the client
io.to(connectorRoom).emit(SocketEvents.CONNECTION_STATE, {
username: SocketUsernames.CONNECTOR,
request_id,
room: sessionId,
to: SocketUsernames.CONNECTOR,
type: SocketEvents.CONNECTION_STATE,
data: false,
})
}
})

// Lidar com mensagens recebidas do cliente
Expand Down
2 changes: 2 additions & 0 deletions packages/socket-server/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './database'
export * from './retryWithBackoff'
1 change: 0 additions & 1 deletion packages/socket-server/src/utils/index.ts.ts

This file was deleted.

52 changes: 52 additions & 0 deletions packages/socket-server/src/utils/retryWithBackoff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))

type RetryOptions = {
retries?: number
baseDelay?: number
}

const DEFAULT_RETRIES = 3
const DEFAULT_BASE_DELAY = 300 // ms

/**
* Executes an async function with retry attempts and exponential backoff for 5xx errors.
*
* - Retries only for HTTP 5xx errors (status >= 500 and < 600).
* - The delay between attempts increases exponentially and includes random jitter.
* - By default, tries up to 3 times and starts with a 500ms delay.
* - Logs the url (if provided) on each retry attempt.
*
* @template T The return type of the async function
* @param fn The async function to execute
* @param url The url related to the request (for logging)
* @param options.retries Maximum number of attempts (default: 3)
* @param options.baseDelay Base delay in ms for exponential backoff (default: 300)
* @returns The result of the async function if any attempt succeeds
* @throws Rethrows the original error if not a 5xx error or if all attempts fail
*/
async function retryWithBackoff<T>(fn: () => Promise<T>, url?: string, { retries = DEFAULT_RETRIES, baseDelay = DEFAULT_BASE_DELAY }: RetryOptions = {}): Promise<T> {
for (let attempt = 0; attempt <= retries; attempt++) {
try {
return await fn()
} catch (error) {
const status = error?.response?.status

if (!status || status < 500 || status >= 600) {
throw error
}

if (attempt === retries) {
throw error
}

const jitter = Math.random() * 100
const delay = baseDelay * Math.pow(2, attempt) + jitter

console.warn(`[RETRY] Attempt ${attempt + 1}/${retries + 1} failed (status ${status})${url ? ` for URL: ${url}` : ''}. Retrying in ${Math.round(delay)}ms...`)

await sleep(delay)
}
}
}

export { retryWithBackoff }
Loading