ftx-api icon indicating copy to clipboard operation
ftx-api copied to clipboard

How to handle "ping / pong" things?

Open alexbuzzz opened this issue 2 years ago • 3 comments

After 1 hour of working, websocket connection crashes. Do I need to response on this: [ 'Websocket event: ', '{"type":"pong"}' ] ?

Is there any method like this or something? client.on('ping', () => { client.pong(); });

alexbuzzz avatar Jul 20 '22 07:07 alexbuzzz

This library already has ping/pong heartbeats in the websocket client. You can monitor them by enabling the "silly" logs: https://github.com/tiagosiebler/ftx-api/blob/master/src/websocket-client.ts#L343-L346

At regular intervals (pingInterval) the library sends upstream pings. The ws client expects to receive a pong frame OR any message on that connection (onWsMessage) before a timer expires (pongTimeout). If neither of those happen in time, the connection is assumed dead and it's respawned automatically after a tear down.

If that happens, you'll see a info log message like this: 'Pong timeout - clearing timers & closing socket to reconnect'

What happens when you say it crashes? Can you reproduce this with every topic or only specific ones?

tiagosiebler avatar Jul 20 '22 09:07 tiagosiebler

Here is my error below. I don't know what is it. It happens after 1 hour of running.

node:internal/process/promises:265 triggerUncaughtException(err, true /* fromPromise */); ^ AxiosError: getaddrinfo ENOTFOUND ftx.com at GetAddrInfoReqWrap.onlookup [as oncomplete] (node:dns:71:26) { hostname: 'ftx.com', syscall: 'getaddrinfo', code: 'ENOTFOUND', errno: -3008, config: { transitional: { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false }, adapter: [Function: httpAdapter], transformRequest: [ [Function: transformRequest] ], transformResponse: [ [Function: transformResponse] ], timeout: 0, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, maxBodyLength: -1, env: { FormData: [Function: FormData] { LINE_BREAK: '\r\n', DEFAULT_CONTENT_TYPE: 'application/octet-stream' } }, validateStatus: [Function: validateStatus], headers: { Accept: 'application/json, text/plain, */*', 'User-Agent': 'axios/0.27.2' }, url: 'https://ftx.com/api/futures', method: 'get', data: undefined }, request: <ref *3> Writable { _writableState: WritableState { objectMode: false, highWaterMark: 16384, finalCalled: false, needDrain: false, ending: false, ended: false, finished: false, destroyed: false, decodeStrings: true, defaultEncoding: 'utf8', length: 0, writing: false, corked: 0, sync: true, bufferProcessing: false, onwrite: [Function: bound onwrite], writecb: null, writelen: 0, afterWriteTickInfo: null, buffered: [], bufferedIndex: 0, allBuffers: true, allNoop: true, pendingcb: 0, constructed: true, prefinished: false, errorEmitted: false, emitClose: true, autoDestroy: true, errored: null, closed: false, closeEmitted: false, [Symbol(kOnFinished)]: [] }, _events: [Object: null prototype] { response: [Function: handleResponse], error: [Function: handleRequestError], socket: [Function: handleRequestSocket] }, _eventsCount: 3, _maxListeners: undefined, _options: { maxRedirects: 21, maxBodyLength: 10485760, protocol: 'https:', path: '/api/futures', method: 'GET', headers: { Accept: 'application/json, text/plain, */*', 'User-Agent': 'axios/0.27.2' }, agent: undefined, agents: { http: undefined, https: undefined }, auth: undefined, hostname: 'ftx.com', port: null, nativeProtocols: { 'http:': { _connectionListener: [Function: connectionListener], METHODS: [ 'ACL', 'BIND', 'CHECKOUT', 'CONNECT', 'COPY', 'DELETE', 'GET', 'HEAD', 'LINK', 'LOCK', 'M-SEARCH', 'MERGE', 'MKACTIVITY', 'MKCALENDAR', 'MKCOL', 'MOVE', 'NOTIFY', 'OPTIONS', 'PATCH', 'POST', 'PROPFIND', 'PROPPATCH', 'PURGE', 'PUT', 'REBIND', 'REPORT', 'SEARCH', 'SOURCE', 'SUBSCRIBE', 'TRACE', 'UNBIND', 'UNLINK', 'UNLOCK', 'UNSUBSCRIBE' ], STATUS_CODES: { '100': 'Continue', '101': 'Switching Protocols', '102': 'Processing', '103': 'Early Hints', '200': 'OK', '201': 'Created', '202': 'Accepted', '203': 'Non-Authoritative Information', '204': 'No Content', '205': 'Reset Content', '206': 'Partial Content', '207': 'Multi-Status', '208': 'Already Reported', '226': 'IM Used', '300': 'Multiple Choices', '301': 'Moved Permanently', '302': 'Found', '303': 'See Other', '304': 'Not Modified', '305': 'Use Proxy', '307': 'Temporary Redirect', '308': 'Permanent Redirect', '400': 'Bad Request', '401': 'Unauthorized', '402': 'Payment Required', '403': 'Forbidden', '404': 'Not Found', '405': 'Method Not Allowed', '406': 'Not Acceptable', '407': 'Proxy Authentication Required', '408': 'Request Timeout', '409': 'Conflict', '410': 'Gone', '411': 'Length Required', '412': 'Precondition Failed', '413': 'Payload Too Large', '414': 'URI Too Long', '415': 'Unsupported Media Type', '416': 'Range Not Satisfiable', '417': 'Expectation Failed', '418': "I'm a Teapot", '421': 'Misdirected Request', '422': 'Unprocessable Entity', '423': 'Locked', '424': 'Failed Dependency', '425': 'Too Early', '426': 'Upgrade Required', '428': 'Precondition Required', '429': 'Too Many Requests', '431': 'Request Header Fields Too Large', '451': 'Unavailable For Legal Reasons', '500': 'Internal Server Error', '501': 'Not Implemented', '502': 'Bad Gateway', '503': 'Service Unavailable', '504': 'Gateway Timeout', '505': 'HTTP Version Not Supported', '506': 'Variant Also Negotiates', '507': 'Insufficient Storage', '508': 'Loop Detected', '509': 'Bandwidth Limit Exceeded', '510': 'Not Extended', '511': 'Network Authentication Required' }, Agent: [Function: Agent] { defaultMaxSockets: Infinity }, ClientRequest: [Function: ClientRequest], IncomingMessage: [Function: IncomingMessage], OutgoingMessage: [Function: OutgoingMessage], Server: [Function: Server], ServerResponse: [Function: ServerResponse], createServer: [Function: createServer], validateHeaderName: [Function: __node_internal_], validateHeaderValue: [Function: __node_internal_], get: [Function: get], request: [Function: request], maxHeaderSize: [Getter], globalAgent: [Getter/Setter] }, 'https:': { Agent: [Function: Agent], globalAgent: Agent { _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, defaultPort: 443, protocol: 'https:', options: [Object: null prototype], requests: [Object: null prototype] {}, sockets: [Object: null prototype], freeSockets: [Object: null prototype] {}, keepAliveMsecs: 1000, keepAlive: false, maxSockets: Infinity, maxFreeSockets: 256, scheduling: 'lifo', maxTotalSockets: Infinity, totalSocketCount: 1, maxCachedSessions: 100, _sessionCache: [Object], [Symbol(kCapture)]: false }, Server: [Function: Server], createServer: [Function: createServer], get: [Function: get], request: [Function: request] } }, pathname: '/api/futures' }, _ended: true, _ending: true, _redirectCount: 0, _redirects: [], _requestBodyLength: 0, _requestBodyBuffers: [], _onNativeResponse: [Function (anonymous)], _currentRequest: <ref *1> ClientRequest { _events: [Object: null prototype] { response: [Function: bound onceWrapper] { listener: [Function (anonymous)] }, abort: [Function (anonymous)], aborted: [Function (anonymous)], connect: [Function (anonymous)], error: [Function (anonymous)], socket: [Function (anonymous)], timeout: [Function (anonymous)] }, _eventsCount: 7, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: true, chunkedEncoding: false, shouldKeepAlive: false, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: false, sendDate: false, _removedConnection: false, _removedContLen: false, _removedTE: false, _contentLength: 0, _hasBody: true, _trailer: '', finished: true, _headerSent: true, _closed: false, socket: <ref *2> TLSSocket { _tlsOptions: { allowHalfOpen: undefined, pipe: false, secureContext: SecureContext { context: SecureContext {} }, isServer: false, requestCert: true, rejectUnauthorized: true, session: Buffer(1674) [Uint8Array] [ 48, 130, 6, 134, 2, 1, 1, 2, 2, 3, 4, 4, 2, 19, 2, 4, 32, 125, 161, 63, 174, 86, 115, 34, 207, 73, 71, 212, 236, 196, 60, 230, 85, 253, 31, 6, 34, 137, 177, 90, 103, 240, 189, 183, 31, 40, 19, 190, 177, 4, 48, 241, 165, 217, 10, 1, 58, 144, 132, 167, 202, 165, 92, 36, 223, 231, 48, 208, 236, 0, 138, 116, 85, 133, 42, 144, 158, 211, 4, 87, 70, 249, 145, 120, 98, 212, 243, 136, 206, 247, 34, 144, 172, 102, 40, 101, 24, 145, 238, 161, ... 1574 more items ], ALPNProtocols: undefined, requestOCSP: undefined, enableTrace: undefined, pskCallback: undefined, highWaterMark: undefined, onread: undefined, signal: undefined }, _secureEstablished: false, _securePending: false, _newSessionPending: false, _controlReleased: true, secureConnecting: true, _SNICallback: null, servername: null, alpnProtocol: null, authorized: false, authorizationError: null, encrypted: true, _events: [Object: null prototype] { close: [ [Function: onSocketCloseDestroySSL], [Function], [Function: onClose], [Function: socketCloseListener] ], end: [ [Function: onConnectEnd], [Function: onReadableStreamEnd] ], newListener: [Function: keylogNewListener], connect: [ [Function], [Function], [Function] ], secure: [Function: onConnectSecure], session: [Function (anonymous)], free: [Function: onFree], timeout: [Function: onTimeout], agentRemove: [Function: onRemove], error: [Function: socketErrorListener], drain: [Function: ondrain] }, _eventsCount: 11, connecting: false, _hadError: true, _parent: null, _host: 'ftx.com', _readableState: ReadableState { objectMode: false, highWaterMark: 16384, buffer: BufferList { head: null, tail: null, length: 0 }, length: 0, pipes: [], flowing: true, ended: false, endEmitted: false, reading: true, constructed: true, sync: false, needReadable: true, emittedReadable: false, readableListening: false, resumeScheduled: false, errorEmitted: true, emitClose: false, autoDestroy: true, destroyed: true, errored: Error: getaddrinfo ENOTFOUND ftx.com at GetAddrInfoReqWrap.onlookup [as oncomplete] (node:dns:71:26) { errno: -3008, code: 'ENOTFOUND', syscall: 'getaddrinfo', hostname: 'ftx.com' }, closed: true, closeEmitted: true, defaultEncoding: 'utf8', awaitDrainWriters: null, multiAwaitDrain: false, readingMore: false, dataEmitted: false, decoder: null, encoding: null, [Symbol(kPaused)]: false }, _maxListeners: undefined, _writableState: WritableState { objectMode: false, highWaterMark: 16384, finalCalled: false, needDrain: false, ending: false, ended: false, finished: false, destroyed: true, decodeStrings: false, defaultEncoding: 'utf8', length: 132, writing: true, corked: 0, sync: false, bufferProcessing: false, onwrite: [Function: bound onwrite], writecb: [Function: bound onFinish], writelen: 132, afterWriteTickInfo: null, buffered: [], bufferedIndex: 0, allBuffers: true, allNoop: true, pendingcb: 1, constructed: true, prefinished: false, errorEmitted: true, emitClose: false, autoDestroy: true, errored: Error: getaddrinfo ENOTFOUND ftx.com at GetAddrInfoReqWrap.onlookup [as oncomplete] (node:dns:71:26) { errno: -3008, code: 'ENOTFOUND', syscall: 'getaddrinfo', hostname: 'ftx.com' }, closed: true, closeEmitted: true, [Symbol(kOnFinished)]: [] }, allowHalfOpen: false, _sockname: null, _pendingData: 'GET /api/futures HTTP/1.1\r\n' + 'Accept: application/json, text/plain, */*\r\n' + 'User-Agent: axios/0.27.2\r\n' + 'Host: ftx.com\r\n' + 'Connection: close\r\n' + '\r\n', _pendingEncoding: 'latin1', server: undefined, _server: null, ssl: null, _requestCert: true, _rejectUnauthorized: true, parser: null, _httpMessage: [Circular *1], [Symbol(res)]: TLSWrap { _parent: TCP { reading: [Getter/Setter], onconnection: null, [Symbol(owner_symbol)]: [Circular *2], [Symbol(handle_onclose)]: [Function: done] }, _parentWrap: undefined, _secureContext: SecureContext { context: SecureContext {} }, reading: false, onkeylog: [Function: onkeylog], onhandshakestart: {}, onhandshakedone: [Function (anonymous)], onocspresponse: [Function: onocspresponse], onnewsession: [Function: onnewsessionclient], onerror: [Function: onerror], [Symbol(owner_symbol)]: [Circular *2] }, [Symbol(verified)]: false, [Symbol(pendingSession)]: null, [Symbol(async_id_symbol)]: 336098, [Symbol(kHandle)]: null, [Symbol(kSetNoDelay)]: false, [Symbol(lastWriteQueueSize)]: 0, [Symbol(timeout)]: null, [Symbol(kBuffer)]: null, [Symbol(kBufferCb)]: null, [Symbol(kBufferGen)]: null, [Symbol(kCapture)]: false, [Symbol(kBytesRead)]: 0, [Symbol(kBytesWritten)]: 0, [Symbol(connect-options)]: { rejectUnauthorized: true, ciphers: 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA', checkServerIdentity: [Function: checkServerIdentity], minDHSize: 1024, session: Buffer(1674) [Uint8Array] [ 48, 130, 6, 134, 2, 1, 1, 2, 2, 3, 4, 4, 2, 19, 2, 4, 32, 125, 161, 63, 174, 86, 115, 34, 207, 73, 71, 212, 236, 196, 60, 230, 85, 253, 31, 6, 34, 137, 177, 90, 103, 240, 189, 183, 31, 40, 19, 190, 177, 4, 48, 241, 165, 217, 10, 1, 58, 144, 132, 167, 202, 165, 92, 36, 223, 231, 48, 208, 236, 0, 138, 116, 85, 133, 42, 144, 158, 211, 4, 87, 70, 249, 145, 120, 98, 212, 243, 136, 206, 247, 34, 144, 172, 102, 40, 101, 24, 145, 238, 161, ... 1574 more items ], maxRedirects: 21, maxBodyLength: 10485760, protocol: 'https:', path: null, method: 'GET', headers: { Accept: 'application/json, text/plain, */*', 'User-Agent': 'axios/0.27.2' }, agent: undefined, agents: { http: undefined, https: undefined }, auth: undefined, hostname: 'ftx.com', port: 443, nativeProtocols: { 'http:': [Object], 'https:': [Object] }, pathname: '/api/futures', _defaultAgent: Agent { _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, defaultPort: 443, protocol: 'https:', options: [Object: null prototype], requests: [Object: null prototype] {}, sockets: [Object: null prototype], freeSockets: [Object: null prototype] {}, keepAliveMsecs: 1000, keepAlive: false, maxSockets: Infinity, maxFreeSockets: 256, scheduling: 'lifo', maxTotalSockets: Infinity, totalSocketCount: 1, maxCachedSessions: 100, _sessionCache: [Object], [Symbol(kCapture)]: false }, host: 'ftx.com', servername: 'ftx.com', _agentKey: 'ftx.com:443:::::::::::::::::::::', encoding: null, singleUse: true } }, _header: 'GET /api/futures HTTP/1.1\r\n' + 'Accept: application/json, text/plain, */*\r\n' + 'User-Agent: axios/0.27.2\r\n' + 'Host: ftx.com\r\n' + 'Connection: close\r\n' + '\r\n', _keepAliveTimeout: 0, _onPendingData: [Function: nop], agent: Agent { _events: [Object: null prototype] { free: [Function (anonymous)], newListener: [Function: maybeEnableKeylog] }, _eventsCount: 2, _maxListeners: undefined, defaultPort: 443, protocol: 'https:', options: [Object: null prototype] { path: null }, requests: [Object: null prototype] {}, sockets: [Object: null prototype] { 'ftx.com:443:::::::::::::::::::::': [ [TLSSocket] ] }, freeSockets: [Object: null prototype] {}, keepAliveMsecs: 1000, keepAlive: false, maxSockets: Infinity, maxFreeSockets: 256, scheduling: 'lifo', maxTotalSockets: Infinity, totalSocketCount: 1, maxCachedSessions: 100, _sessionCache: { map: { 'ftx.com:443:::::::::::::::::::::': [Buffer [Uint8Array]], 'otc.ftx.com:443:::::::::::::::::::::': [Buffer [Uint8Array]] }, list: [ 'ftx.com:443:::::::::::::::::::::', 'otc.ftx.com:443:::::::::::::::::::::' ] }, [Symbol(kCapture)]: false }, socketPath: undefined, method: 'GET', maxHeaderSize: undefined, insecureHTTPParser: undefined, path: '/api/futures', _ended: false, res: null, aborted: false, timeoutCb: null, upgradeOrConnect: false, parser: null, maxHeadersCount: null, reusedSocket: false, host: 'ftx.com', protocol: 'https:', _redirectable: [Circular *3], [Symbol(kCapture)]: false, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype] { accept: [ 'Accept', 'application/json, text/plain, */*' ], 'user-agent': [ 'User-Agent', 'axios/0.27.2' ], host: [ 'Host', 'ftx.com' ] } }, _currentUrl: 'https://ftx.com/api/futures', [Symbol(kCapture)]: false } } [nodemon] app crashed - waiting for file changes before starting...

alexbuzzz avatar Jul 20 '22 10:07 alexbuzzz

AxiosError: getaddrinfo ENOTFOUND ftx.com

Looks like your network connection dropped, so it couldn't reach FTX servers.

url: 'https://ftx.com/api/futures', method: 'get'

Is your code executing listAllFutures()? https://github.com/tiagosiebler/ftx-api/blob/master/src/rest-client.ts#L186-L188

There's many errors talking about that GET endpoint from the REST API.

tiagosiebler avatar Jul 20 '22 10:07 tiagosiebler

is it possible to receive pong events and also disable pong logging (looks to be on by default)?

two-crows avatar Oct 22 '22 05:10 two-crows

You can disable the whole logging level (or customise it with your own logic) by injecting a custom logger into the websocket client:

const { WebsocketClient, DefaultLogger } = require('ftx-api');

// Disable all logging on the silly level
const customLogger = {
  ...DefaultLogger,
  silly: () => {},
};

const ws = new WebsocketClient(
  wsOptions,
  customLogger
);

Regarding receiving pong events, the connector already handles pings and pong receipts for you, as well as reconnects if a pong doesn't arrive on time.

What's the use case for wanting to receive them too?

tiagosiebler avatar Oct 22 '22 08:10 tiagosiebler

just keeping track of the last websocket event received. it's not a must have for the pong events but was just curious. appreciate the response.

two-crows avatar Oct 23 '22 04:10 two-crows

just keeping track of the last websocket event received. it's not a must have for the pong events but was just curious. appreciate the response.

I see, I'm open to exposing something if you want a more granular view. Could be a lastEventAt timestamp, for example, if that's useful?

In the meantime you could also use the injected logger to also use log events as a sign a ws event came through. If you take a look at the ws client (specifically onWsMessage), you can see the different things you can expect.

tiagosiebler avatar Oct 23 '22 09:10 tiagosiebler