Description
Problem Statement
Currently for browser offline caching support there is the Offline
integration which has a number of issues:
- It does not cater for events that fail to send due to network issues
- Browser
navigator.onLine
does not offer a reliable way to determine if the machine is connected to the internet. It only tells you that the machine is connected to a network.- For example, on Windows, the Docker virtual network adaptor means that
onLine
is alwaystrue
😭
- For example, on Windows, the Docker virtual network adaptor means that
- Because it sends delayed events via
hub.captureEvent()
this results in another pass through the integrations which triggers theDedupe
integration Can't get Offline integration to work #3046 (comment).- Multiple passes through integrations and event processors opens up a lot of issues and confusion
Solution Brainstorm
It makes more sense for offline support to act at the transport interface so it can catch send failures. Since the transports have recently been re-written in v7 with a functional approach this has become simpler.
const makeOfflineFetchTransport = makeOfflineTransport(makeFetchTransport);
Additionally allow the backing store to be configurable:
const makeOfflineFetchTransport = makeOfflineTransport(makeFetchTransport, indexedDbStore(MAX_QUEUE_LENGTH));
The issue with wrapping Transport
directly is that send
doesn't pass through rate limit responses so there is no way to wrap that directly and retry for those failures:
export interface Transport {
send(request: Envelope): PromiseLike<void>;
flush(timeout?: number): PromiseLike<boolean>;
}
To get around this current limitation, the Electron SDK offline transport wraps the default TransportRequestExecutor
and adds offline persisting. This allows us to queue and retry events that have failed due to rate limit errors.
Is this desired in the browser? Should we be retying rate-limited requests?
We could change send
to this without it being a breaking change (#6626):
send(request: Envelope): PromiseLike<void | TransportMakeRequestResponse>;
Then all the existing transports can pass through the response.
Another solution would be to add a new function although this won't currently be able to wrap the existing transports because they don't expose/export their TransportRequestExecutor
implementations:
export function createOfflineTransport(
options: InternalBaseTransportOptions,
makeRequest: TransportRequestExecutor,
store: PersistStore = makeIndexedDbStore(DEFAULT_TRANSPORT_BUFFER_SIZE),
): Transport {
//
}