We made IndexedDB 10x faster
TLDR
Faster queries when using IndexedDB, consistently improving TypeScript support, publish a new 1.0 manifesto and more.
IndexedDB: still durable, but 10x faster
IndexedDB is great for durably storing data in the browser - it's widely supported, easily introspected with development tools, and has a rich API relative to other web standard storage options. However, it can be a performance bottleneck if your application frequently reads and writes. Because Triplit is committed to offering as many robust client-side storage options as we can, we've tweaked our IndexedDB access patterns to make it as fast as possible. But in many cases, it just isn't fast enough for the performance that highly interactive client applications demand.
To that end, we've added an optional memory cache layer to IndexedDB. It sits in front of the IndexedDB layer and keeps a copy of its data in memory. This leads to 10x faster reads than from IndexedDB directly. The main tradeoff is initialization time, as the memory cache will be populated from the underlying database table when the client is created. This is a one-time cost, and after that, all reads are from the memory cache. In our testing, it is often faster to initialize the memory cache and run queries against it than to run the same queries against IndexedDB directly, so app startup time is often improved as well.
This new feature will be enabled by default. To opt out of it, you need to set the useCache
option in your Triplit client configuration to false
:
import { TriplitClient } from '@triplit/client';
const client = new TriplitClient({
storage: {
type: 'indexeddb',
options: { useCache: false },
},
});
Type fixups
We're always looking for ways to raise our very high bar for TypeScript support. This week, we've added a much-requested type fixup that allows using variables to store repeatedly used filters in your schema. Previously, this caused a type error. Here's an example:
import { Schema as S } from '@triplit/client';
const userIsOwner = ['ownerId', '=', '$token.sub'] as const;
const schema = S.Collections({
posts: {
schema: S.Schema({
id: S.Id(),
ownerId: S.String(),
content: S.String(),
}),
permissions: {
read: { filter: [userIsOwner] },
insert: { filter: [userIsOwner] },
update: { filter: [userIsOwner] },
delete: { filter: [userIsOwner] },
},
},
});
Triplit 1.0 blog
If you're eager to read more, we just published a new blog post summarizing all of the power and magic of Triplit 1.0. We go over all of the special sauce that makes Triplit a great choice for your next project, and mention features that even existing developers may be unaware of. Check it out here.
Other Improvements
- Added additional error logs for outgoing updates to the server
- Fixed an issue where a client coming online with a populated cache could become out of sync with the server if its outbox had data
- Fixed an issue where high-frequency optimistic updates could cause the client to become out of sync with the server