SessionStore size - Bloat in sessionstore.js from Google and others causing major UI freezes

Randell Jesup <[email protected]> Tue, 19 Jul 2011 10:51:12 -0400
Newsgroups gmane.comp.mozilla.performance
Message-ID <[email protected]>
[ This seemed long for in-bug discussion...  (And it's a pain to edit 
there.)  We can move it back there if people wish. ]

As identified in bug 669034 (and bug 669603), we have a serious problem 
with bloat in sessionstore.js caused largely by websites storing large 
amounts of data in the dom sessionstorage.  See 
http://dev.w3.org/html5/webstorage/#dom-sessionstorage for more info. 
(My apologies for the naming confusion between "sessionstore.js" and 
"sessionstorage".  Not my doing. :-)

For example, in a fresh profile browsing to google.com and typing two 
words causes a 366KB sessionstore.js file.  Having Google anywhere in 
the history of a tab typically uses 200-700KB of data.  sessionstore.js 
files of 5, 10, 20 and even 50MB are not unheard of.

This is made worse by the fact that sessionstore.js is a single, huge 
JSON object.  That means that on every save (and they occur a few 
seconds after you navigate, scroll, or type), you get a burst of 
activity to gather all this info, serialize it in JSON (taking a LOT of 
temporary memory, and with larger ones causing GCs), and write it all to 
disk (which is at least handled as a background task).  The memory-use 
spikes are clearly visible (temporary usage of 3x+ the file size).

This also causes BAD delays in UI performance - starting with stutters, 
and working up to 5-15 second freezes(!)

In larger profiles/sessionstores, this leads to NS_ERROR_OUT_OF_MEMORY
trying to save profiles after it's been running a little while, perhaps 
due to VM fragmentation or allocations that are just too large (I 
haven't traced down the source yet as I don't have  debugger set up on 
the machine I run into it most with).  At that point it stops saving 
data, but continues to try and cause freezes.

We should also consider what the target for sessionstore in Electrolysis 
is, and if possible ease that work by hitting the right solution (or one 
on the path to what E10s needs) now.

Options:
a) Convert sessionstore to SQLite, totally.

    Updates can be relatively small, though a single tab might have
    several sessionstore-heavy items in it and so be moderately large (a
    few meg)

    Variation: unchanged history items may be stored independently and
    so minor scrolling/navigation changes might be very small.  Note
    that we'd want to reference history pages by absolute depth, not
    relative.

    Downside: downgrade/revert will be bad (though support could be
    rolled in over 2 releases to ease the problem).  Could cause
    problems for Aurora/Beta users.

b) Separate sessionstore data from session restore data.  The
    sessionstore data is much larger and doesn't change as often; store
    it in a separate file (JSON or SQLite) when it changes (keep a dirty
    bit).

    Danger of synchronization issues between sessionstore.js and the new
    file.

    If a JSON file it will be one big file and need to be rewritten
    pretty often, negating the gains.

c) Don't store sessionstore data if larger than X.  I think there are
    lots of problems with this, and it would cause no end of user
    confusion/frustration, and annoyance of web authors, and hurt our
    'brand'

d) Push overhead to the startup read from the write side

    Use a pseudo-journal, as was mentioned  Write only dirty tabs, and
    write them with a version that increments. On Read, load only the
    most recent version.  This will require occasional rewrites in order
    to discard older updates, or a more sophisticated memory
    allocation-like file structure to overwrite older updates with ones
    that fit in the (unused) space.

    Plus: fast, low/very-low overhead for the most common operation
          (write after typing or scrolling).
    Minus: extra disk usage, more complex housekeeping, slightly slower
           startup.

    Added possibility: write updates as deltas.  Saves space/time,
    reduces housekeeping, slightly slows reading probably (but smaller
    files helps), more complex writing.

    This option amounts to a poor-man's filesystem (or if you prefer, a
    poor-man's DB).

e) Push the processing of the per-tab data over to a background
    thread/process.

    May play into work with Electrolysis.  Not necessarily at odds with
    other options above.  Fetching data from each tab would be the
    primary UI-blocker, and it would only block long enough to pass the
    data for one tab to the sessionstore background process.  At the
    cost of memory, could keep data for tabs around and fetch data only
    from those that indicate dirty state.

Others?  Any ideas out there?

Several of these options (e, f, etc) could be implemented using a 
memory-mapped file (or rather a pair of files that it flips between for 
crash-safety).  This would allow easy caching/reordering/modification of 
the session data, though those sorts of operations may cause significant 
disk IO.

P.S.  It's nice to be back (heavily active and later a driver in the 
0.9-1.2ish days), and nice to sink my teeth into a few of these meaty 
problems.

-- 
Randell Jesup, Mozilla Corporation
Remove ".news" for personal email