npm dexie 4.0.1-alpha.17
Dexie v4.0.1-alpha.17

latest releases: 4.0.4, 4.0.3, 4.0.2...
11 months ago

This release replaces 4.0.1-alpha.12, 4.0.1-alpha.13 and 4.0.1-alpha.14 due to issues #1730, #1731 and a few other issues fixed in PR #1733

Cache and Optimistic Updates (v0.1)

The biggest news with this release is the initial support for common cache for live queries that enables multiple liveQuery observables targeting the same set of queries to reuse a single request towards indexedDB and to be immediately triggered as soon as data is being mutated. In practice, this will become a big game changer for large apps that has many ongoing liveQueries going on targeting large amount of data and possibly targeting the same data from different components.

Before the flow was pretty simple but could take some time from a mutation until a change was visible in the app if the app had many components using liveQuery and where the results of the queries were large.

Flow prior to this relase:

  1. All live queries for the app is run initially (and the components are rendered).
  2. User does som action so that data is being updated (such as db.friends.update(1, {age: 23}).
  3. Write-transaction is committed --> onstoragemutated event is broadcasted to all tabs and workers
  4. liveQueries that has touched the affected ranges are re-executed. If we have 10 components with affected liveQueries, all 10 of them will simultanously query IndexedDB for their data again.

Flow with this release:

  1. All live queries for the app is run initially their promises are immediately cached in memory so that several liveQueries requesting the same data will await the same single promise instead of each one of them requesting indexedDB. When result arrives, the result is kept in the cache as long as there are components subscribing to the query.
  2. User does som action so that data is being updated (such as db.friends.update(1, {age: 23}). The cache is immediately recomputed by applying the mutation to the previous result and triggering subscribers immediately to rerender.
  3. Mutation promise resolves or reject. If it rejects, the optimistic update is rolled back and components rerender.
  4. Transaction commits or rejects. If it rejects, the optimistic updates are rolled back and components rerender. If it was successful, components doesn't need to requery indexedDB because they already have the changes in the cache.
  5. All mutations are still broadcasted to other tabs and workers. When a mutation comes from another tab or worker, it will not be optimistic but require a re-execution of the query.

So basically, liveQueries will only request IndexedDB at startup and two identical queries will only result in a single query towards IndexedDB.

Now this is the initial version of this and only certain queries can utilize this:

  • If your liveQuery callback explicitely does db.transaction('r', ...), the user expects isolation, so optimistic updates won't be used.
  • Only simple range queries are supported in this initial version: equals, above, aboveOrEqual, below, belowOrEqual, between, startsWith.
  • Only tables with inbound keys are supported.
  • Only toArray() queries (not .keys(), .primaryKeys() or .count())
  • Not offset based queries.
  • Not ignoreCase queries
  • Not anyOf or inAnyRange

Example of optimised queries:

  • db.friends.toArray()
  • db.friends.where('age').between(18,65, true, true).limit(10).toArray()

Example of non-optimized queries in this initial version:

  • db.friends.primaryKeys()
  • db.friends.count()
  • db.friends.offset(25).toArray()
  • db.transaction('r', db.friends, () => db.friends.toArray())

PRs:

  • #1718 Optimistic updates
  • #1727 Fix optimistic updates

Other changes:

  • Dexie will throw if trying to mutate data from a liveQuery callback ( liveQuery(()=>db.friends.add({...})) )
  • A new constructor option to Dexie is {cache: 'immutable' | 'cloned' | 'disabled'}. The default is 'cloned' but 'immutable' would give better performance because the cached results can be returned directly to the caller without having to deep clone it. However, the data will be frozen so if the liveQuery callback would try to set properties or call mutating array operations on the results, it will fail to do so if having { cache: 'immutable' }. array.sort() is one example of a commonly used mutable operation. Workaround is to use array.slice().sort() instead.

Other PRs in this release

  • #1711 Add .d.mts files to support import types properly.
  • #1716 Remove custom unhandledrejection event propagation

Don't miss a new dexie release

NewReleases is sending notifications on new releases.