Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarification around offline & retry support #1419

Open
jpalmowski opened this issue Nov 3, 2020 · 13 comments
Open

Clarification around offline & retry support #1419

jpalmowski opened this issue Nov 3, 2020 · 13 comments
Assignees

Comments

@jpalmowski
Copy link

Hi I'm just seeking some clarification around the behaviour I'm seeing when the browser is offline

  1. I have a simple test app that logs an exception on load. (Angular app, not using plugin)
  2. When I'm offline AI stores the exception as expected in SessionStorage[AI_Buffer]
  3. After maxBatchInterval AI attempts to send the batch which fails
  4. The failed batch is cleared out of SessionStorage[AI_sentBuffer] but is never added back to SessionStorage[AI_Buffer] for retry

I can see code for exponential back off on retries so I'm assuming this should be a supported scenario but wanted to verify before I debugged any further.

I'm also seeking any information on if/when localStorage is going to be supported. It seems like it used to exist (#602) and I can see some lower level util code to support it - Given your architecture is pluggable and the interfaces for session and local storage are the same I'm wondering why this hasn't been included?

My config if it helps:

config: {
        instrumentationKey: 'APP_KEY',
        enableAutoRouteTracking: false, // option to log all route changes,
        enableSessionStorageBuffer: true,
        isRetryDisabled: false,
        disableDataLossAnalysis: false
      }

@MSNev
Copy link
Collaborator

MSNev commented Nov 13, 2020

Hi @jpalmowski, sorry for the delay. As you have already seen, looking into the code the retry backoff should be kicking in to delay the retry operation (based on your isRetryDisabled: false (which is also the default)). But this check occurs after this one
if ((xhr.status < 200 || xhr.status >= 300) && xhr.status !== 0) {

So if the XHR implementation is returning something that satisifes this check that it's likely why the request is getting dropped.
Do (or can) you get the XHR result code returned (assuming via the F12 browser), an alternative would be to load the debug plugin (https://github.com/microsoft/ApplicationInsights-JS/tree/master/extensions/applicationinsights-debugplugin-js) which listens to the events that might provide some additional information about why the event is getting dropped.

LocalStorage: We currently don't have any scheduled work to bring LocalStorage into the App Insights codebase, so I don't have a current timeframe for when this might occur.

You are correct though in that because of the general design it's possible, and we do have an internal set of extensions that supports both LocalStorage and IndexedDb but at this point it doesn't work with the App Insights Sender. There is a general plan to move these internal plugins into the public space, but again no committed timeframe.

@Bort-777
Copy link

Hi, @MSNev
We are using the SDK for handling errors/events in ReactNative (mobile), but looks that it designed for online usage on browser. Supporting offline mode on mobile is actual and have to implemented in other way than cookie/localStorage management. Could you provide any plans about offline behaviour customisation or tips how to implement it on out side if possible.

@MSNev
Copy link
Collaborator

MSNev commented Dec 5, 2022

Yes, the SDK has a general assumption that it will be online all or most of the time and I don't believe that we have any general plans to assume complete offline or only online sometimes.

Currently the SDK (by default) stores events into SessionStorage (controlled by enableSessionStorageBuffer) to provide some level of resiliency between page loads (ie. the same session).

However, this generally doesn't work for Mobile / complete offline scenarios. For those it would need the LocalStorage or IndexedDb support mentioned above as well as some refactoring of the current online / offline listener (currently only used for handling retries)

All of this is current managed in the Sender.ts (channel).

As part of the SDK, it is built on a "plugin" model (every component is a plugin -- including the Sender.ts), so you "could" implement an OfflinePlugin which when offline simply stored the events into some form of persistent storage and then just not call the "nextPlugin" (thus avoiding the Sender also trying to send the events). And once it detects that it's back online it could simply pickup to persisted events and send them on down the plugin chain there are some internal helpers which you should be able to "recreate" the plugin chain between instances or simply just call track again and when your plugin gets called again you just bypass your logic. If you want to go down this path I can provide some more details.

As you mentioned ReactNative, this also seems like something that could be contributed (added) to the ReactNative extension as an optional capability.

@zolakt
Copy link

zolakt commented Feb 28, 2023

Can someone please help me understand this?

I have a similar scenario.
I have an Ionic app that sometimes looses internet connection.
While there is no connection I show an error screen with a countdown timer. After the timer expires, I reload the page with location.reload(). This happens every 30 seconds. So, eventually, the app will reload in an online state sooner or later. Sometimes the connection can be down for a while (before it is restored). Lets say it takes an hour. I'd like to collect all the errors during that time and sent them to the server once the connection is restored. So, basically I'd like to buffer around 120 error messages during that hour (one every 30 seconds).

However, on every refresh I can clearly see that the AI_buffer field in session storage is cleared.
Then the page refreshes, fails again, and adds a new item to the buffer (which is empty on refresh).
So effectively, I never have more than 1 item in the buffer.

Isn't the whole point of session storage that is should persist between refreshes of the same tab?

Maybe this is a Ionic/webview thing, but it doesn't seem so. e.g. I can see the session_id in the cookie changing on every refresh. It's a little difficult to test this without Ionic (or some kind of pwa). So I wanted to ask first, is this normal, that the AI-buffer field is cleared on every refresh? If so, why?

@MSNev
Copy link
Collaborator

MSNev commented Feb 28, 2023

The Send buffer should be storing the values in the Session Storage and only clear them out after they have been sent.

It also does in fact have an online / offline listener here which (should) be causing the events to be retried.

If the events are getting dropped, then your probably hitting the eventsLimitInMem (defaults to 10,000! ???) or the session based storage is not enabled of failed (because it's used too much)....

The Sender probably should be changed to not attempt to send requests when offline, and I recall that there is a retry limit (can't find the config ATM).

Another option might be to pause the sender based on your own offline listener... And as part of this maybe only reload your page if the browser reports that your online.

@zolakt
Copy link

zolakt commented Mar 1, 2023

I'm not really following, honestly. The problem is that the send buffer is almost always empty.
Maybe I didn't explain this clearly. I've made a video, if it will helps.

For testing purposes I've set the maxBatchInterval to 10 seconds (normally I want this set to 0).
The app is offline during the whole video.

  1. The page loads. It can't ping the server and displays this error screen. The item is added to AI_buffer, as expected
  2. When the countdown reaches 10, it tries to send it to app insights server and fails, as expected. Interestingly, you can see that it moves the item to AI_sentBuffer for a split second, and then immediately moves it back to AI_buffer
  3. When the countdown reaches 0, it refreshes the page with location.reload(). You can see that both buffers get completely cleared at this point. Nothing gets persisted, which is the main problem here. It even looks like something is explicitly clearing them, not the refresh itself, but I could be wrong about this
  4. The loop repeats. It again can't ping the server, and puts a new item in AI_buffer. Since nothing is persisting, the AI_buffer always has either 0 or 1 items.

So, I don't have an issue with eventsLimitInMem, since I never have more that 1 item in the buffer. And I doubt sessions storage is used too much.

DevTools.-.localhost_.2023-03-01.11-14-50-compressed.mp4

peitschie added a commit to peitschie/ApplicationInsights-JS that referenced this issue Apr 6, 2023
This can be used like:

```
import { LocalStorageSendBuffer } from "@microsoft/applicationinsights-channel-js";

const appInsights = new ApplicationInsights(...);
appInsights.getSender().setBuffer(LocalStorageSendBuffer);
```
peitschie added a commit to peitschie/ApplicationInsights-JS that referenced this issue Apr 6, 2023
This can be used like:

```
const appInsights = new ApplicationInsights({
  config: {
    enableSessionStorageBuffer: true,
    bufferOverride: "localStorage"
  }
});
```
peitschie added a commit to peitschie/ApplicationInsights-JS that referenced this issue Apr 7, 2023
…osoft#1419

This can be used like:

```
const appInsights = new ApplicationInsights({
  config: {
    enableSessionStorageBuffer: true,
    bufferOverride: {
      getItem: (logger, key) => localStorage.getItem(key),
      setItem: (logger, key, value) => localStorage.setItem(key, value),
    }
  }
});
```
peitschie added a commit to peitschie/ApplicationInsights-JS that referenced this issue Apr 7, 2023
…osoft#1419

This can be used like:

```
const appInsights = new ApplicationInsights({
  config: {
    enableSessionStorageBuffer: true,
    bufferOverride: {
      getItem: (logger, key) => localStorage.getItem(key),
      setItem: (logger, key, value) => localStorage.setItem(key, value),
    }
  }
});
```
peitschie added a commit to peitschie/ApplicationInsights-JS that referenced this issue Apr 7, 2023
…osoft#1419

This can be used like:

```
const appInsights = new ApplicationInsights({
  config: {
    enableSessionStorageBuffer: true,
    bufferOverride: {
      getItem: (logger, key) => localStorage.getItem(key),
      setItem: (logger, key, value) => localStorage.setItem(key, value),
    }
  }
});
```
peitschie added a commit to peitschie/ApplicationInsights-JS that referenced this issue Apr 7, 2023
…osoft#1419

This can be used like:

```
const appInsights = new ApplicationInsights({
  config: {
    enableSessionStorageBuffer: true,
    bufferOverride: {
      getItem: (logger, key) => localStorage.getItem(key),
      setItem: (logger, key, value) => localStorage.setItem(key, value),
    }
  }
});
```
MSNev added a commit that referenced this issue Apr 11, 2023
#2037)

This can be used like:

```
const appInsights = new ApplicationInsights({
  config: {
    enableSessionStorageBuffer: true,
    bufferOverride: {
      getItem: (logger, key) => localStorage.getItem(key),
      setItem: (logger, key, value) => localStorage.setItem(key, value),
    }
  }
});
```

Co-authored-by: Nev <54870357+MSNev@users.noreply.github.com>
MSNev added a commit that referenced this issue Apr 11, 2023
#2037)

This can be used like:

```
const appInsights = new ApplicationInsights({
  config: {
    enableSessionStorageBuffer: true,
    bufferOverride: {
      getItem: (logger, key) => localStorage.getItem(key),
      setItem: (logger, key, value) => localStorage.setItem(key, value),
    }
  }
});
```

Co-authored-by: Nev <54870357+MSNev@users.noreply.github.com>
MSNev added a commit that referenced this issue Apr 12, 2023
* [BUG] The documentation for enableDebug is incorrect, it should reference enableDebugExceptions #2014 (#2022)

* [BUG] SDK LOAD Failure reporting not working #2027 (#2038)

* Add a simple interface to enable custom buffer storage solutions #1419 (#2037)

This can be used like:

```
const appInsights = new ApplicationInsights({
  config: {
    enableSessionStorageBuffer: true,
    bufferOverride: {
      getItem: (logger, key) => localStorage.getItem(key),
      setItem: (logger, key, value) => localStorage.setItem(key, value),
    }
  }
});
```

Co-authored-by: Nev <54870357+MSNev@users.noreply.github.com>

* [Master] Add readme documentation for IStorageBuffer (#2045)

* [Release] Increase version to 2.8.12 (#2046)

---------

Co-authored-by: Philip Peitsch <philip.peitsch@gmail.com>
@peitschie
Copy link
Contributor

@zolakt a recent patch has been merged (2.8.12+ & 3+) which might allow you to address the issue you're seeing here.

By default, AppInsights uses session storage, which is cleared whenever the tab/browser is reloaded, among other conditions. Possibly, it's worth seeing if switching to local storage resolves the issue you're seeing here?

Instructions for doing this are outlined on #2037

const appInsights = new ApplicationInsights({
  config: {
    enableSessionStorageBuffer: true,
    bufferOverride: { 
      getItem: (logger, key) => localStorage.getItem(key),
      setItem: (logger, key, value) => localStorage.setItem(key, value),
    }
  }
});

@MSNev MSNev assigned Karlie-777 and unassigned MSNev Oct 27, 2023
@MSNev
Copy link
Collaborator

MSNev commented Oct 27, 2023

FYI - We are now working on a new "extension" which may also help with this.
The extension is being designed to allow all telemetry emitted while "offline" to be stored in either IndexedDb or LocalStorage (based on the coming new OfflineChannel configuration), we have started working on the components and plan to have it available by the end of Jan'24, portions will be available via the nightly builds as we make progress.

@MSNev
Copy link
Collaborator

MSNev commented Oct 27, 2023

If you want to see the PR's this (should) identify them

@augusthjerrild
Copy link

augusthjerrild commented Feb 23, 2024

Hi @MSNev :-)

Will this latest release (3.1.0), which came out last week, make it possible to support offline situations in a react-native app?
Will it work out of the box, or do I need to make some special kind of configuration before it will work offline, besides upgrading the RN Plugin :-)? Very excited about the new offline support!

@MSNev
Copy link
Collaborator

MSNev commented Feb 23, 2024

Will this latest release (3.1.0), which came out last week, make it possible to support offline situations in a react-native app?

Depending on your detection of whether your offline -- maybe (not tried this)

Will it work out of the box, or do I need to make some special kind of configuration before it will work offline

No, It's not automatic you will need to also include the new Offline Channel which is used to "store" the batched events into LocalStorage / IndexedDb.

There are a couple of additional changes which you will need as well as currently we only have a very basic IOfflineListener implementation (which is only listening to the browser events). We updated the interface to "handle" 2 levels of offline but we don't have any production implementation for this yet.

The 2 levels are

  • The runtime "event" (which really signals that the host has a network connection)
  • Manual (currently) switching to tell the SDK that while is has a network connection (because the above says it does) it should consider itself offline because it doesn't have any internet access. This is intended to handle being on a local network which is currently disconnected.

@Karlie-777 is leading the development of the Offline Channel and we still need to provide more documentation around how to use it -- And, yes we are still making improvements, so please feel free to provide feedback.

@p-hlp
Copy link

p-hlp commented Mar 5, 2024

Great work on the 3.1 release! @MSNev @Karlie-777 I'm sure If you'd be willing to nudge us in the right direction with a minimal example how to implement theIOfflineListener and/or use the offline channel, I'd bet some people (including me) would give this a go (even if it means nightly builds for now). Happy to contribute docs / give feedback.

@Karlie-777
Copy link
Contributor

Thanks @p-hlp I will update the documentation soon!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants