Skip to content

A "hybrid" storage model with GRDB backend

Charles Wright requested to merge cvwright/yagni-storage into main

A slightly different approach to local on-device storage, based on what we learned from the first two iterations.

This branch proposes a "hybrid" storage model to (1) work around issues with re-entrancy in the database, (2) allow loading Rooms with low latency and (3) support "huge" rooms -- those that have either lots of messages or lots of state (or both). It's partially inspired by Matrix's approach for "sliding sync" where they incrementally load in data from the server.

The reeentrancy problem: When loading a Room, we also need to load some Events. But the mechanism that GRDB provides to run transactions makes this messy; before, our load functions needed to know whether they're the top level of the transaction, or if they're being run as a sort of subordinate call in order to load some higher-level object.

Here we avoid the reentrancy issues by borrowing an idea that we developed when considering switching to a simpler key/value database instead of SQLite. We were thinking about just sticking the whole dang Room (Events and all) into a single blob in the key/value store.

We don't go quite that crazy here.

  • Store some events as a blob in the Room record to enable fast loading from a single record.
    • One blob for an array of state events that contain the basic room info. Creation, naming, encryption, ... etc. Just the stuff that we need to know in order to show the user a list of rooms.
    • One blob for an array of the most recent messages. Currently we only include one, but it could be more. Like above, this is intended to give the application just enough info to show an overview of the room.
  • Leave most ALL of the room's events in a separate table, to be loaded on demand later. which we also query in order to load the room.

Other changes:

  • Change the Room's internal state representation to actually match the Matrix spec. It's supposed to be a map from (EventType,String) to event content. Before, we had only the EventType in the key, and we were storing the event contents as an Array. So we would fail to overwrite part of the state when we were supposed to update it with a new value, keeping both around in a weird inconsistent way. Now new state events with the same EventType and state key will overwrite the old values.

Update: After many iterations back and forth, I finally decided to go back to the minimalist version of the state event storage. We now have a single source of truth for room state: the state events table. This limits our ability to query the rooms from the Room table, but oh well. I suppose if we want to fetch rooms by type or creator etc, we can always query the state events table directly.

Edited by Charles Wright

Merge request reports

Loading