replace Redis with an embedded SQLite string->string KV store (group commit, in-tx events, SQL search)
Build and Push Docker Image / build (push) Successful in 2m44s

This commit is contained in:
Worph
2026-10-19 20:51:00 +00:00
parent c53146e514
commit dc5cbdfb9b
80 changed files with 4601 additions and 6077 deletions
+3 -3
View File
@@ -215,9 +215,9 @@ export default function Dashboard() {
}
/>
<Tile
label="Redis keys"
label="Store keys"
value={formatCount(stats?.redisKeys)}
note={stats?.redisMemory ? `${stats.redisMemory} in use` : undefined}
note={stats?.redisMemory ? `${stats.redisMemory} on disk` : undefined}
/>
<Tile
label="Identities"
@@ -239,7 +239,7 @@ export default function Dashboard() {
{health.status === 'ok' ? 'Healthy' : health.status}
</div>
<p style={noteStyle}>
Redis: {health.redis ? 'Connected' : 'Disconnected'}
Metadata store: {health.redis ? 'Open' : 'Unavailable'}
</p>
</>
) : (
+5 -5
View File
@@ -179,7 +179,7 @@ async function fetchAccounts(): Promise<Account[]> {
// Resolve an account's display name from the User Data Layer, or null.
//
// Never throws and never reports an error upward: a name is decoration on top
// of the uid, and the account list must render identically whether Redis is up
// of the uid, and the account list must render identically whether the store is up
// (identity itself is file-backed, the UDL is not). The name is also *unsigned*
// — meta-core stores the plaintext beside the signed record and verifies
// nothing — so the uid stays on screen as the real identity, and the length cap
@@ -209,7 +209,7 @@ async function fetchDisplayName(uid: string): Promise<string | null> {
// so per-row calls would walk the keyspace once per account.
//
// Never throws, for the same reason fetchDisplayName does not: the counts are
// context on top of the uid, and the list must render identically when Redis is
// context on top of the uid, and the list must render identically when the store is
// down — accounts are file-backed, their data is not.
//
// Returns null on failure rather than an empty map. The difference is
@@ -603,7 +603,7 @@ function AccountCard({
records === 1 ? 'record' : 'records'
} — likes, ratings, watch progress across ${titles.toLocaleString()} ${
titles === 1 ? 'title' : 'titles'
} — are permanently deleted from Redis, along with every index that names this uid.`}{' '}
} — are permanently deleted from the metadata store, along with every index that names this uid.`}{' '}
This cannot be undone and there is no backup.
</li>
<li>
@@ -655,12 +655,12 @@ export default function Identity() {
return;
}
if (gen !== reqGen.current) return;
// Render the list before resolving names, so a down Redis delays nothing.
// Render the list before resolving names, so a down store delays nothing.
setAccounts(list);
setLoading(false);
// Names and counts both come after the list is on screen, and neither can
// fail the page: a down Redis costs decoration, not the account list.
// fail the page: a down store costs decoration, not the account list.
const [settled, counts] = await Promise.all([
Promise.allSettled(list.map((a) => fetchDisplayName(a.uid))),
fetchStats(),