skip to Main Content

I’m trying to build a user replay session functionality for a website and I’m using rrweb library to do that.

What this library does is when recording: it captures all the events in the webpage and I’m able to save those events by storing them in an array and when I want to replay the session I’ll just pass the array to the replay function and that function handles the session replay.

Currently for testing purposes I’m saving this array in my sessionStorage and every time a new event is emitted I’ll just get the array then push that new event into it then save the array again in my sessionStorage:

rrweb.record({
    emit(event) {
        const sessions = JSON.parse(sessionStorage.getItem('sessions')) || [];
        sessions.push(event);
        sessionStorage.setItem('sessions', JSON.stringify(sessions));
    },
});

However, for production, instead of saving that array in my sessionStorage and then updating it every time a new event is emitted, I would like to save that array in my database and I want to call the function that would save the array to my database once either when the user logs out or when the user decides to close the website (like by pressing the X button).

The first part -when the user logs out– is pretty straight forward, I’ll just add an eventListener on the logout button, it’s the second part -when the user decides to close the website- that is giving me some headache.

I know that there is the beforeUnload event however after a quick search it became clear to me that it’s unreliable so basically what I’m looking for is a reliable way to determine when the user closed my website so I could fire an async function that would save the array to my database.

2

Answers


  1. For this kind of problem you should make a database schema to store this and fire a function that takes the event and save that events in the database as usual.
    And for another problem of the after closing website you may call a function like:

        window.onunload=()=>{
          //function to save the event in the database
    }
    

    May this will help you for you.

    Login or Signup to reply.
  2. Here are my thoughts on what you can do to mitigate the unreliableness of the beforeunload event:

    1. When the page is initially loaded get and save the current number of entries in your sessions array. Assuming we are talking about a single page that you wish to replay, this count could be saved as a JavaScript variable eventCount. Better yet would be to get the count from the server-side table in case for some reason not all the events recorded were successfully saved the last time the page was closed.
    2. Arrange for a function checkEvents to be called every N seconds (you must decide how often you want to call this function) using the Window setInterval method. This function will look at the current event count (variable newCount) and if greater than the current eventCount value, then the Navigator sendBeacon method can be used to POST a request to the server passing all the events that have been added since the last call (i.e. JSON.stringify(sessions.slice(eventCount, newCount)) and when the request completes assign eventCount = newCount if newCount is > eventCount. Note that while the asynchronous sendBeacon request is running it is possible that new events have been generated and that’s why we update the event count with newCount rather than the current size of the sessions array.
    3. You will need to do a final sendBeacon request when the page is unloaded. Since we know that the beforeunload and unload events are not reliable, then we use the visibiltychanged event (if supported by the browser) and when the new visibility state is ‘hidden’ we update the server. The documentation discusses those user actions that cause this event to be fired (not just on closing the page). If, however, the browser does not support this event, then the pagehide event will have to be used.

    It’s not discussed within the documentation for the Navigator.sendBeacon method whether there can be multiple concurrent requests. Assuming there can be (it’s an asynchronous call), there is slight possibility the user might decide to navigate away from the page or close it while a sendBeacon request is currently in progress because of the setInterval call. Then the server URL to which you post this request should probably lock the table while doing the insertions so that any subsequent POST to this URL will block until the previous one completes. Another solution I would propose if your table uses some sort of sequence number as the primary key would be to also pass to the server the starting array index of the first event being passed and the server would use that to explicitly set the sequence number for each event inserted. Then concurrent updates could run without having to lock the entire table.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search