@earth-app/collegedb 1.0.8 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # CollegeDB
2
2
 
3
- _Cloudflare D1 Horizontal Sharding Router_
3
+ Universal Database Horizontal Sharding Router
4
4
 
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
6
6
  [![GitHub Issues](https://img.shields.io/github/issues/earth-app/CollegeDB)](https://github.com/earth-app/CollegeDB/issues)
@@ -8,7 +8,32 @@ _Cloudflare D1 Horizontal Sharding Router_
8
8
  [![GitHub License](https://img.shields.io/github/license/earth-app/CollegeDB)](LICENSE)
9
9
  ![NPM Version](https://img.shields.io/npm/v/%40earth-app%2Fcollegedb)
10
10
 
11
- A TypeScript library for **true horizontal scaling** of SQLite-style databases on Cloudflare using D1 and KV. CollegeDB distributes your data across multiple D1 databases, with each table's records split by primary key across different database instances.
11
+ A TypeScript library for **true horizontal scaling** of SQLite-style databases primarily for Cloudflare using D1 and KV, with additional provider adapters for Redis/Valkey KV and PostgreSQL/MySQL/SQLite SQL backends, plus Drizzle ORM interop across those SQL providers. CollegeDB distributes your data across multiple database shards, with each table's records split by primary key across different database instances.
12
+
13
+ ## Table of Contents
14
+
15
+ - [Why CollegeDB](#why-collegedb)
16
+ - [Features](#features)
17
+ - [Getting Started](#getting-started)
18
+ - [Benchmark Suite](#benchmark-suite)
19
+ - [Provider Adapters](#provider-adapters)
20
+ - [NuxtHub + Drizzle Recipes](#nuxthub--drizzle-recipes)
21
+ - [Sandbox Benchmarks (Docker Compose)](#sandbox-benchmarks-docker-compose)
22
+ - [Basic Usage](#basic-usage)
23
+ - [Multi-Key Shard Mappings](#multi-key-shard-mappings)
24
+ - [Drop-in Replacement for Existing Databases](#drop-in-replacement-for-existing-databases)
25
+ - [Troubleshooting](#troubleshooting)
26
+ - [API Reference](#api-reference)
27
+ - [Architecture](#architecture)
28
+ - [Cloudflare Setup](#cloudflare-setup)
29
+ - [Monitoring and Maintenance](#monitoring-and-maintenance)
30
+ - [Performance Analysis](#performance-analysis)
31
+ - [Advanced Configuration](#advanced-configuration)
32
+ - [Quick Reference](#quick-reference)
33
+ - [Contributing](#contributing)
34
+ - [License](#license)
35
+
36
+ ## Why CollegeDB
12
37
 
13
38
  CollegeDB implements **data distribution** where a single logical table is physically stored across multiple D1 databases:
14
39
 
@@ -39,30 +64,19 @@ This allows you to:
39
64
  - **Scale geographically** by placing shards in different regions
40
65
  - **Increase write throughput** by parallelizing across multiple database instances
41
66
 
42
- ## 📈 Overview
67
+ ## Features
43
68
 
44
- CollegeDB provides a sharding layer on top of Cloudflare D1 databases, enabling you to:
69
+ - Automatic query routing (primary key to shard mapping)
70
+ - Provider adapters for Redis/Valkey/NuxtHub KV plus PostgreSQL/MySQL/SQLite SQL
71
+ - Drizzle interop through existing SQL providers (`createPostgreSQLProvider`, `createMySQLProvider`, `createSQLiteProvider`)
72
+ - Hyperdrive helpers for PostgreSQL and MySQL
73
+ - Multiple allocation strategies: round-robin, random, hash, location-aware, and mixed read/write strategies
74
+ - Durable Object shard coordination and shard statistics
75
+ - Migration helpers for integrating existing datasets and rebalancing mappings
45
76
 
46
- - **Scale horizontally** by distributing table data across multiple D1 instances
47
- - **Route queries automatically** based on primary key mappings
48
- - **Maintain consistency** with KV-based shard mapping
49
- - **Optimize for geography** with location-aware shard allocation
50
- - **Monitor and rebalance** shard distribution
51
- - **Handle migrations** between shards seamlessly
77
+ ## Getting Started
52
78
 
53
- ## 📦 Features
54
-
55
- - **🔀 Automatic Query Routing**: Primary key → shard mapping using Cloudflare KV
56
- - **🎯 Multiple Allocation Strategies**: Round-robin, random, hash-based, and location-aware distribution
57
- - **🔄 Mixed Strategy Support**: Different strategies for reads vs writes (e.g., location for writes, hash for reads)
58
- - **📊 Shard Coordination**: Durable Objects for allocation and statistics
59
- - **🛠 Migration Support**: Move data between shards with zero downtime
60
- - **🔄 Automatic Drop-in Replacement**: Zero-config integration with existing databases
61
- - **🤖 Smart Migration Detection**: Automatically discovers and maps existing data
62
- - **⚡ High Performance**: Optimized for Cloudflare Workers runtime
63
- - **🔧 TypeScript First**: Full type safety and excellent DX
64
-
65
- ## Installation
79
+ ### Installation
66
80
 
67
81
  ```bash
68
82
  bun add @earth-app/collegedb
@@ -70,6 +84,373 @@ bun add @earth-app/collegedb
70
84
  npm install @earth-app/collegedb
71
85
  ```
72
86
 
87
+ ### NuxtHub + Drizzle with CollegeDB Routing
88
+
89
+ Keep NuxtHub + Drizzle for schema/migrations and add CollegeDB as your routing layer.
90
+
91
+ ```typescript
92
+ import { db as hubDb } from '@nuxthub/db';
93
+ import { kv } from '@nuxthub/kv';
94
+ import { sql } from 'drizzle-orm';
95
+ import { drizzle } from 'drizzle-orm/d1';
96
+ import { createNuxtHubKVProvider, createSQLiteProvider, first, initialize, run } from '@earth-app/collegedb';
97
+
98
+ let initialized = false;
99
+
100
+ function ensureCollegeDB(env: { DB_SECONDARY: D1Database }) {
101
+ if (initialized) return;
102
+
103
+ initialize({
104
+ kv: createNuxtHubKVProvider(kv),
105
+ shards: {
106
+ 'db-primary': createSQLiteProvider(hubDb, sql),
107
+ 'db-secondary': createSQLiteProvider(drizzle(env.DB_SECONDARY), sql)
108
+ },
109
+ strategy: 'hash'
110
+ });
111
+
112
+ initialized = true;
113
+ }
114
+
115
+ export default defineEventHandler(async (event) => {
116
+ const env = event.context.cloudflare.env;
117
+ ensureCollegeDB(env);
118
+
119
+ await run('post:123', 'INSERT OR REPLACE INTO blog_posts (id, title) VALUES (?, ?)', ['post:123', 'Hello from CollegeDB']);
120
+
121
+ const post = await first<{ id: string; title: string }>('post:123', 'SELECT id, title FROM blog_posts WHERE id = ?', ['post:123']);
122
+
123
+ return { post };
124
+ });
125
+ ```
126
+
127
+ ### Drop-in Pattern for Existing `hub:db` + `hub:kv` Code
128
+
129
+ ```typescript
130
+ // before
131
+ import { eq } from 'drizzle-orm';
132
+ import { db } from 'hub:db';
133
+ import { kv } from 'hub:kv';
134
+ import { blogPosts } from '~/server/db/schema';
135
+
136
+ const cached = await kv.get('nuxtpress:post:slug');
137
+ if (cached) return cached;
138
+
139
+ const rows = await db.select().from(blogPosts).where(eq(blogPosts.slug, slug)).limit(1);
140
+ await kv.set('nuxtpress:post:slug', rows[0], { ttl: 3600 });
141
+ ```
142
+
143
+ ```typescript
144
+ // after (CollegeDB routing + same NuxtHub KV cache)
145
+ import { kv } from '@nuxthub/kv';
146
+ import { sql } from 'drizzle-orm';
147
+ import { db } from 'hub:db';
148
+ import { createNuxtHubKVProvider, createSQLiteProvider, first, initialize } from '@earth-app/collegedb';
149
+
150
+ let initialized = false;
151
+
152
+ function setup() {
153
+ if (initialized) return;
154
+ initialize({
155
+ kv: createNuxtHubKVProvider(kv),
156
+ shards: {
157
+ 'db-primary': createSQLiteProvider(db, sql)
158
+ },
159
+ strategy: 'hash'
160
+ });
161
+ initialized = true;
162
+ }
163
+
164
+ setup();
165
+
166
+ const cacheKey = `nuxtpress:post:${slug}`;
167
+ const cached = await kv.get(cacheKey);
168
+ if (cached) return cached;
169
+
170
+ const row = await first<{ id: string; slug: string; title: string }>(
171
+ cacheKey,
172
+ 'SELECT id, slug, title FROM blog_posts WHERE slug = ? LIMIT 1',
173
+ [slug]
174
+ );
175
+
176
+ await kv.set(cacheKey, row, { ttl: 3600 });
177
+ ```
178
+
179
+ ## Benchmark Suite
180
+
181
+ CollegeDB includes a benchmark runner that executes each SQL+KV combination across adapter profiles, then generates a report with profile-specific matrices.
182
+
183
+ ### Adapter Profiles
184
+
185
+ | Profile | Purpose |
186
+ | ---------- | ----------------------------------------------------------------------- |
187
+ | native | Direct provider clients (Cloudflare bindings or driver-native adapters) |
188
+ | drizzle | Drizzle interop through SQL provider adapters |
189
+ | hyperdrive | Hyperdrive connection-string wrappers for PostgreSQL/MySQL |
190
+ | nuxthub | NuxtHub-style KV adapter with SQL provider interop |
191
+
192
+ ### Scenario Catalog
193
+
194
+ | Scenario Key | Scenario | What Happens | Workload Per Run |
195
+ | ----------------- | -------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
196
+ | basic_crud | Basic CRUD round-trip | Insert, read, update, and delete a user via routed queries. | 20 iterations; 4 routed SQL ops per iteration |
197
+ | advanced_usage | Advanced lookup workflow | Writes user+post, adds lookup aliases, then validates join and alias-based lookup. | 15 iterations; ~5 routed SQL ops + KV lookup-key updates per iteration |
198
+ | migration_mapping | Migration-style mapping creation | Inserts legacy records on a fixed shard, then builds shard mappings in batch and validates routing. | 10 iterations; 20 legacy records mapped per iteration |
199
+ | bulk_crud | Bulk CRUD pressure | Performs bulk inserts, half updates, and full delete sweep, then validates shard-wide totals. | 7 iterations; 160 inserts + 80 updates + 160 deletes per iteration |
200
+ | indexing | Indexed query scan | Creates an index on posts(user_id) and repeatedly queries the indexed path. | 15 iterations after warmup dataset build |
201
+ | metadata_fetch | Metadata inspection | Reads table metadata/introspection rows from one shard. | 14 iterations; 1 metadata query per iteration |
202
+ | pragma_or_info | PRAGMA / server info | Runs provider-specific PRAGMA/info query to sample low-level metadata latency. | 14 iterations; 1 pragma/info query per iteration |
203
+ | counting | Cross-shard counting | Counts users across all shards to measure fanout aggregation overhead. | 14 iterations; all-shard count aggregation per iteration |
204
+ | shard_fanout | Shard fanout query | Runs query fanout to all shards and aggregates shard-level responses. | 14 iterations; 1 all-shards query per iteration |
205
+ | reassignment | Shard reassignment flow | Creates a record, reassigns it to another shard, and verifies routed reads still succeed. | 10 iterations; insert + reassignment + verification per iteration |
206
+
207
+ ### Report Matrices
208
+
209
+ Each generated report includes:
210
+
211
+ - `Matrix: SQL x KV (Overall)`
212
+ - `Matrix: Adapter Profiles (Overall Avg)`
213
+ - `Matrix: Core Scenario Latency (avg/p95)`
214
+ - `Matrix: Introspection and Routing Latency (avg/p95)`
215
+ - `Cloudflare Worker (wrangler dev --local)`
216
+ - `Matrix: Cloudflare Adapter Profiles (Overall Avg)`
217
+
218
+ ### Common Commands
219
+
220
+ ```bash
221
+ bun run test:sandbox
222
+ bun run test:sandbox:drizzle
223
+ bun run test:sandbox:nuxthub
224
+ bun run test:sandbox:hyperdrive
225
+ ```
226
+
227
+ For Docker-based benchmark details and filtering options, see [Sandbox Benchmarks (Docker Compose)](#sandbox-benchmarks-docker-compose).
228
+
229
+ ## Provider Adapters
230
+
231
+ CollegeDB can run with either native Cloudflare bindings or custom providers as long as they match the exported `KVStorage` and `SQLDatabase` interfaces.
232
+
233
+ Drizzle interop is enabled by passing a Drizzle `sql` tag as the optional second argument to `createPostgreSQLProvider`, `createMySQLProvider`, or `createSQLiteProvider`.
234
+
235
+ Supported adapters:
236
+
237
+ - `createRedisKVProvider`
238
+ - `createValkeyKVProvider`
239
+ - `createNuxtHubKVProvider`
240
+ - `createPostgreSQLProvider`
241
+ - `createMySQLProvider`
242
+ - `createSQLiteProvider`
243
+ - `createDrizzleSQLProvider` (compatibility helper)
244
+ - `createHyperdrivePostgresProvider`
245
+ - `createHyperdriveMySQLProvider`
246
+
247
+ ```typescript
248
+ import { createClient as createRedisClient } from 'redis';
249
+ import { Pool } from 'pg';
250
+ import { createPostgreSQLProvider, createRedisKVProvider, initialize, run, type CollegeDBConfig } from '@earth-app/collegedb';
251
+
252
+ const redisClient = createRedisClient({ url: process.env.REDIS_URL });
253
+ const pgPool = new Pool({ connectionString: process.env.POSTGRES_URL });
254
+
255
+ const config: CollegeDBConfig = {
256
+ kv: createRedisKVProvider(redisClient),
257
+ shards: {
258
+ 'pg-east': createPostgreSQLProvider(pgPool)
259
+ },
260
+ strategy: 'hash',
261
+ disableAutoMigration: true
262
+ };
263
+
264
+ async function bootstrap() {
265
+ await redisClient.connect();
266
+ initialize(config);
267
+ await run('user-1', 'INSERT INTO users (id, name) VALUES (?, ?)', ['user-1', 'Taylor']);
268
+ }
269
+
270
+ bootstrap().catch(console.error);
271
+ ```
272
+
273
+ For Hyperdrive-backed SQL connections, use `createHyperdrivePostgresProvider` or `createHyperdriveMySQLProvider` with your database client factory.
274
+
275
+ ### NuxtHub Runtime Example (D1 + KV)
276
+
277
+ ```typescript
278
+ import { db } from '@nuxthub/db';
279
+ import { kv } from '@nuxthub/kv';
280
+ import { sql } from 'drizzle-orm';
281
+ import { createNuxtHubKVProvider, createSQLiteProvider, initialize, run, first } from '@earth-app/collegedb';
282
+
283
+ initialize({
284
+ kv: createNuxtHubKVProvider(kv),
285
+ shards: {
286
+ 'db-primary': createSQLiteProvider(db, sql)
287
+ },
288
+ strategy: 'hash'
289
+ });
290
+
291
+ await run('draft:home', 'INSERT OR REPLACE INTO drafts (id, content) VALUES (?, ?)', ['draft:home', '# Home']);
292
+
293
+ const draft = await first<{ id: string; content: string }>('draft:home', 'SELECT id, content FROM drafts WHERE id = ?', ['draft:home']);
294
+ ```
295
+
296
+ ### Keep Drizzle Schema + Migrations, Route Queries with CollegeDB
297
+
298
+ CollegeDB does not replace your Drizzle schema or NuxtHub migration workflow.
299
+
300
+ ```bash
301
+ npx nuxt db generate
302
+ npx nuxt db migrate
303
+ ```
304
+
305
+ Use those migrations as-is, then route runtime reads/writes through CollegeDB adapters.
306
+
307
+ For a complete non-Cloudflare setup, see `examples/provider-sandbox.ts`.
308
+
309
+ ## NuxtHub + Drizzle Recipes
310
+
311
+ ### Multi-Vendor Shards (Cloudflare + Postgres/MySQL)
312
+
313
+ NuxtHub supports multiple deployment/database vendors. CollegeDB can shard across any SQL backends that Drizzle can connect to.
314
+
315
+ ```typescript
316
+ import { sql } from 'drizzle-orm';
317
+ import { drizzle as drizzlePg } from 'drizzle-orm/postgres-js';
318
+ import { drizzle as drizzleMySQL } from 'drizzle-orm/mysql2';
319
+ import { drizzle as drizzleD1 } from 'drizzle-orm/d1';
320
+ import { kv } from '@nuxthub/kv';
321
+ import postgres from 'postgres';
322
+ import mysql from 'mysql2/promise';
323
+ import {
324
+ createMySQLProvider,
325
+ createNuxtHubKVProvider,
326
+ createPostgreSQLProvider,
327
+ createSQLiteProvider,
328
+ initialize,
329
+ run
330
+ } from '@earth-app/collegedb';
331
+
332
+ const pgClient = postgres(process.env.POSTGRES_URL!);
333
+ const mysqlPool = mysql.createPool(process.env.MYSQL_URL!);
334
+
335
+ function setup(env: { DB_CF: D1Database }) {
336
+ initialize({
337
+ kv: createNuxtHubKVProvider(kv),
338
+ shards: {
339
+ 'db-cf': createSQLiteProvider(drizzleD1(env.DB_CF), sql),
340
+ 'db-pg': createPostgreSQLProvider(drizzlePg(pgClient), sql),
341
+ 'db-mysql': createMySQLProvider(drizzleMySQL(mysqlPool), sql)
342
+ },
343
+ strategy: 'hash'
344
+ });
345
+ }
346
+
347
+ export default defineEventHandler(async (event) => {
348
+ setup(event.context.cloudflare.env);
349
+
350
+ await run('tenant:acme:user:1', 'INSERT INTO users (id, name) VALUES (?, ?)', ['tenant:acme:user:1', 'Ada']);
351
+ });
352
+ ```
353
+
354
+ ### Keep NuxtHub Cache/KV Semantics Intact
355
+
356
+ Use NuxtHub KV for app cache while CollegeDB uses its own key namespace for shard mappings:
357
+
358
+ ```typescript
359
+ import { kv } from '@nuxthub/kv';
360
+ import { first } from '@earth-app/collegedb';
361
+
362
+ const cacheKey = `nuxtpress:post:${slug}`;
363
+ const cached = await kv.get(cacheKey);
364
+ if (cached) return cached;
365
+
366
+ const post = await first(cacheKey, 'SELECT * FROM blog_posts WHERE slug = ? LIMIT 1', [slug]);
367
+ await kv.set(cacheKey, post, { ttl: 3600 });
368
+ ```
369
+
370
+ ## Sandbox Benchmarks (Docker Compose)
371
+
372
+ CollegeDB ships with an integration sandbox runner that benchmarks real latency across provider combinations.
373
+
374
+ Requirements:
375
+
376
+ - Docker + Docker Compose plugin
377
+ - Bun
378
+ - Wrangler (installed as a dev dependency and invoked by scripts)
379
+
380
+ The Cloudflare benchmark path runs against the dedicated sandbox worker:
381
+
382
+ - Worker entry: `sandbox/worker.ts`
383
+ - Wrangler config: `sandbox/wrangler.jsonc`
384
+
385
+ Main commands:
386
+
387
+ ```bash
388
+ # Run full SQL x KV matrix plus Cloudflare local benchmark
389
+ bun run test:sandbox
390
+
391
+ # Run full SQL x KV matrix only
392
+ bun run test:sandbox:all
393
+
394
+ # Run Cloudflare local benchmark only (wrangler dev --local)
395
+ bun run test:sandbox:cloudflare
396
+ ```
397
+
398
+ Provider filters:
399
+
400
+ ```bash
401
+ # One SQL provider against all KV providers (native profile by default)
402
+ bun run test:sandbox:mysql
403
+ bun run test:sandbox:postgres
404
+ bun run test:sandbox:mariadb
405
+ bun run test:sandbox:sqlite
406
+
407
+ # One KV provider against all SQL providers (native profile by default)
408
+ bun run test:sandbox:redis
409
+ bun run test:sandbox:valkey
410
+
411
+ # Run all SQL x KV combinations for one adapter profile
412
+ bun run test:sandbox:drizzle
413
+ bun run test:sandbox:nuxthub
414
+ bun run test:sandbox:hyperdrive
415
+
416
+ # Explicit pairwise combinations
417
+ bun run test:sandbox:postgres+redis
418
+ bun run test:sandbox:postgres+valkey
419
+ bun run test:sandbox:mysql+redis
420
+ bun run test:sandbox:mysql+valkey
421
+ bun run test:sandbox:mariadb+redis
422
+ bun run test:sandbox:mariadb+valkey
423
+ bun run test:sandbox:sqlite+redis
424
+ bun run test:sandbox:sqlite+valkey
425
+ ```
426
+
427
+ Output behavior:
428
+
429
+ - Every run writes a timestamped Markdown report to `sandbox/results/`
430
+ - `sandbox/results/latest.md` is always updated to the newest report
431
+ - The runner prints the report in-terminal using Bun's Markdown renderer with ANSI formatting
432
+ - `test:sandbox` includes native, drizzle, hyperdrive, and nuxthub adapter profiles across supported SQL/KV combinations plus Cloudflare profile runs
433
+
434
+ Benchmark coverage includes:
435
+
436
+ - basic CRUD
437
+ - advanced lookup/routing workflows
438
+ - migration-style mapping creation
439
+ - bulk CRUD
440
+ - indexing queries
441
+ - metadata fetch
442
+ - pragma/info queries (provider-specific)
443
+ - counting across shards
444
+ - shard fanout aggregation
445
+ - shard reassignment workflow
446
+
447
+ How to read benchmark rows:
448
+
449
+ - Latency cells are formatted as `average / p95` in milliseconds.
450
+ - `FAILED` means the scenario returned an error.
451
+ - `N/A` means the scenario was intentionally skipped in that environment.
452
+ - Use the detailed section for full `avg`, `p50`, `p95`, `min`, `max`, and sample count (`n`).
453
+
73
454
  ## Basic Usage
74
455
 
75
456
  ```typescript
@@ -88,7 +469,7 @@ collegedb(
88
469
  },
89
470
  async () => {
90
471
  // Create schema on new shards only (existing shards auto-detected)
91
- await createSchema(env['db-new-shard']);
472
+ await createSchema(env['db-new-shard'], 'CREATE TABLE IF NOT EXISTS users (id TEXT PRIMARY KEY, name TEXT, email TEXT)');
92
473
 
93
474
  // Insert data (automatically routed to appropriate shard)
94
475
  await run('user-123', 'INSERT INTO users (id, name, email) VALUES (?, ?, ?)', ['user-123', 'Johnson', 'alice@example.com']);
@@ -222,8 +603,6 @@ collegedb(
222
603
 
223
604
  ### Adding Lookup Keys to Existing Mappings
224
605
 
225
- s
226
-
227
606
  ```typescript
228
607
  const mapper = new KVShardMapper(env.KV);
229
608
 
@@ -529,13 +908,13 @@ for (const [table, pkColumn] of Object.entries(customIntegration)) {
529
908
  }
530
909
  ```
531
910
 
532
- ## 📚 API Reference
911
+ ## API Reference
533
912
 
534
913
  | Function | Description | Parameters |
535
914
  | ------------------------------------------ | ---------------------------------------------------------------- | -------------------------- |
536
915
  | `collegedb(config, callback)` | Initialize CollegeDB, then run a callback | `CollegeDBConfig, () => T` |
537
916
  | `initialize(config)` | Initialize CollegeDB with configuration | `CollegeDBConfig` |
538
- | `createSchema(d1)` | Create database schema on a D1 instance | `D1Database` |
917
+ | `createSchema(db, schema)` | Create schema on a shard database | `SQLDatabase, string` |
539
918
  | `prepare(key, sql)` | Prepare a SQL statement for execution | `string, string` |
540
919
  | `run(key, sql, bindings)` | Execute a SQL query with primary key routing | `string, string, any[]` |
541
920
  | `first(key, sql, bindings)` | Execute a SQL query and return first result | `string, string, any[]` |
@@ -552,17 +931,33 @@ for (const [table, pkColumn] of Object.entries(customIntegration)) {
552
931
  | `getDatabaseSizeForShard(shard)` | Get size of a specific shard in bytes | `string` |
553
932
  | `flush()` | Clear all shard mappings (development only) | `void` |
554
933
 
934
+ ### Provider Adapter Functions
935
+
936
+ | Function | Description | Parameters |
937
+ | ---------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------- |
938
+ | `createRedisKVProvider(client, options?)` | Adapt a Redis client to CollegeDB's `KVStorage` contract | `RedisLikeClient, { scanCount?: number }` |
939
+ | `createValkeyKVProvider(client, options?)` | Adapt a Valkey client to CollegeDB's `KVStorage` contract | `RedisLikeClient, { scanCount?: number }` |
940
+ | `createNuxtHubKVProvider(client)` | Adapt NuxtHub/Unstorage-style KV clients to `KVStorage` | `NuxtHubKVLike` |
941
+ | `createPostgreSQLProvider(client, sqlTag?)` | Adapt PostgreSQL or Drizzle PostgreSQL clients | `PostgresClientLike, sqlTag?` |
942
+ | `createMySQLProvider(client, sqlTag?)` | Adapt MySQL/MariaDB or Drizzle MySQL/MariaDB clients | `MySQLClientLike, sqlTag?` |
943
+ | `createSQLiteProvider(client, sqlTag?)` | Adapt SQLite/D1 or Drizzle SQLite/D1 clients | `SQLiteClientLike, sqlTag?` |
944
+ | `createDrizzleSQLProvider(client, sqlTag)` | Generic Drizzle adapter (optional helper) | `DrizzleClientLike, DrizzleSqlTagLike` |
945
+ | `createHyperdrivePostgresProvider(binding, clientFactory)` | Create a PostgreSQL `SQLDatabase` adapter using a Hyperdrive binding | `HyperdriveBindingLike, HyperdrivePostgresClientFactory` |
946
+ | `createHyperdriveMySQLProvider(binding, clientFactory)` | Create a MySQL `SQLDatabase` adapter using a Hyperdrive binding | `HyperdriveBindingLike, HyperdriveMySQLClientFactory` |
947
+ | `isKVStorage(value)` | Runtime guard for `KVStorage` | `unknown` |
948
+ | `isSQLDatabase(value)` | Runtime guard for `SQLDatabase` | `unknown` |
949
+
555
950
  ### Drop-in Replacement Functions
556
951
 
557
952
  | Function | Description | Parameters |
558
953
  | ----------------------------------------- | ---------------------------------------------- | ------------------------------ |
559
- | `autoDetectAndMigrate(d1, shard, config)` | Automatically detect and migrate existing data | `D1Database, string, config` |
560
- | `checkMigrationNeeded(d1, shard, config)` | Check if database needs migration | `D1Database, string, config` |
561
- | `validateTableForSharding(d1, table)` | Check if table is suitable for sharding | `D1Database, string` |
562
- | `discoverExistingPrimaryKeys(d1, table)` | Find all primary keys in existing table | `D1Database, string` |
563
- | `integrateExistingDatabase(d1, shard)` | Complete drop-in integration of existing DB | `D1Database, string, mapper` |
954
+ | `autoDetectAndMigrate(d1, shard, config)` | Automatically detect and migrate existing data | `SQLDatabase, string, config` |
955
+ | `checkMigrationNeeded(d1, shard, config)` | Check if database needs migration | `SQLDatabase, string, config` |
956
+ | `validateTableForSharding(d1, table)` | Check if table is suitable for sharding | `SQLDatabase, string` |
957
+ | `discoverExistingPrimaryKeys(d1, table)` | Find all primary keys in existing table | `SQLDatabase, string` |
958
+ | `integrateExistingDatabase(d1, shard)` | Complete drop-in integration of existing DB | `SQLDatabase, string, mapper` |
564
959
  | `createMappingsForExistingKeys(keys)` | Create shard mappings for existing keys | `string[], string[], strategy` |
565
- | `listTables(d1)` | Get list of tables in database | `D1Database` |
960
+ | `listTables(d1)` | Get list of tables in database | `SQLDatabase` |
566
961
  | `clearMigrationCache()` | Clear automatic migration cache | `void` |
567
962
 
568
963
  ### Error Handling
@@ -645,15 +1040,19 @@ The main configuration interface supports both single strategies and mixed strat
645
1040
 
646
1041
  ```typescript
647
1042
  interface CollegeDBConfig {
648
- kv: KVNamespace;
1043
+ kv: KVStorage;
649
1044
  coordinator?: DurableObjectNamespace;
650
- shards: Record<string, D1Database>;
1045
+ shards: Record<string, SQLDatabase>;
651
1046
  strategy?: ShardingStrategy | MixedShardingStrategy;
652
1047
  targetRegion?: D1Region;
653
1048
  shardLocations?: Record<string, ShardLocation>;
654
1049
  disableAutoMigration?: boolean; // Default: false
655
1050
  hashShardMappings?: boolean; // Default: true
656
1051
  maxDatabaseSize?: number; // Default: undefined (no limit)
1052
+ mappingCacheTtlMs?: number; // Default: 30000
1053
+ knownShardsCacheTtlMs?: number; // Default: 10000
1054
+ sizeCacheTtlMs?: number; // Default: 30000
1055
+ migrationConcurrency?: number; // Default: 25
657
1056
  }
658
1057
  ```
659
1058
 
@@ -706,7 +1105,7 @@ const mixedStrategyConfig: CollegeDBConfig = {
706
1105
 
707
1106
  ### Database Size Management
708
1107
 
709
- CollegeDB supports automatic size-based shard exclusion to prevent individual shards from becoming too large. This feature helps maintain optimal performance and prevents hitting D1 storage limits.
1108
+ CollegeDB supports automatic size-based shard exclusion to prevent individual shards from becoming too large. This feature helps maintain optimal performance and prevents hitting database storage limits.
710
1109
 
711
1110
  #### Configuration
712
1111
 
@@ -782,12 +1181,12 @@ const config: CollegeDBConfig = {
782
1181
  // "Excluded 2 shards due to size limits: db-east, db-central"
783
1182
  ```
784
1183
 
785
- #### Performance Impact
1184
+ #### Size-Limit Performance Impact
786
1185
 
787
1186
  - **Size Check Frequency**: Only performed during new allocations (not on reads)
788
1187
  - **Query Efficiency**: Uses fast SQLite pragmas (microsecond execution time)
789
1188
  - **Parallel Execution**: Size checks run concurrently across all shards
790
- - **Caching**: No caching implemented to ensure accurate real-time limits
1189
+ - **Caching**: Size checks are cached in-memory (controlled by `sizeCacheTtlMs`, default `30000`)
791
1190
 
792
1191
  ### Types
793
1192
 
@@ -796,6 +1195,13 @@ CollegeDB exports TypeScript types for better development experience and type sa
796
1195
  | Type | Description | Example |
797
1196
  | ----------------------- | ------------------------------ | --------------------------------------------------- |
798
1197
  | `CollegeDBConfig` | Main configuration object | `{ kv, shards, strategy }` |
1198
+ | `KVStorage` | Provider-agnostic KV contract | `createRedisKVProvider(redisClient)` |
1199
+ | `SQLDatabase` | Provider-agnostic SQL contract | `createPostgreSQLProvider(pgPool)` |
1200
+ | `NuxtHubKVLike` | NuxtHub/Unstorage KV contract | `createNuxtHubKVProvider(kv)` |
1201
+ | `DrizzleClientLike` | Minimal Drizzle DB contract | `createPostgreSQLProvider(drizzleDb, sql)` |
1202
+ | `DrizzleSqlTagLike` | Drizzle SQL tag contract | `createSQLiteProvider(drizzleDb, sql)` |
1203
+ | `QueryResult` | Standard query response shape | `{ success, results, meta }` |
1204
+ | `QueryResultMeta` | Query execution metadata | `{ duration, changes?, last_row_id? }` |
799
1205
  | `ShardingStrategy` | Single strategy options | `'hash' \| 'location' \| 'round-robin' \| 'random'` |
800
1206
  | `MixedShardingStrategy` | Mixed strategy configuration | `{ read: 'hash', write: 'location' }` |
801
1207
  | `OperationType` | Database operation types | `'read' \| 'write'` |
@@ -829,7 +1235,7 @@ const config: CollegeDBConfig = {
829
1235
  };
830
1236
  ```
831
1237
 
832
- ## 🏗 Architecture
1238
+ ## Architecture
833
1239
 
834
1240
  ```txt
835
1241
  ┌─────────────────────────────────────────────────────────────┐
@@ -900,7 +1306,7 @@ const config: CollegeDBConfig = {
900
1306
  - **Random**: Random shard selection for load balancing
901
1307
  - **Location**: Geographic proximity-based allocation for optimal latency
902
1308
 
903
- ## 🌐 Cloudflare Setup
1309
+ ## Cloudflare Setup
904
1310
 
905
1311
  ### 1. Create D1 Databases
906
1312
 
@@ -918,81 +1324,127 @@ wrangler d1 create collegedb-central
918
1324
  wrangler kv namespace create "KV"
919
1325
  ```
920
1326
 
921
- ### 3. Configure wrangler.toml
922
-
923
- ```toml
924
- [[d1_databases]]
925
- binding = "db-east"
926
- database_name = "collegedb-east"
927
- database_id = "your-database-id"
1327
+ ### 3. Configure wrangler.jsonc
928
1328
 
929
- [[d1_databases]]
930
- binding = "db-west"
931
- database_name = "collegedb-west"
932
- database_id = "your-database-id"
933
-
934
- [[kv_namespaces]]
935
- binding = "KV"
936
- id = "your-kv-namespace-id"
937
-
938
- [[durable_objects.bindings]]
939
- name = "ShardCoordinator"
940
- class_name = "ShardCoordinator"
1329
+ ```jsonc
1330
+ {
1331
+ "$schema": "./node_modules/wrangler/config-schema.json",
1332
+ "name": "collegedb-app",
1333
+ "main": "src/index.ts",
1334
+ "compatibility_date": "2026-04-15",
1335
+ "d1_databases": [
1336
+ {
1337
+ "binding": "db-east",
1338
+ "database_name": "collegedb-east",
1339
+ "database_id": "your-east-database-id"
1340
+ },
1341
+ {
1342
+ "binding": "db-west",
1343
+ "database_name": "collegedb-west",
1344
+ "database_id": "your-west-database-id"
1345
+ }
1346
+ ],
1347
+ "kv_namespaces": [
1348
+ {
1349
+ "binding": "KV",
1350
+ "id": "your-kv-namespace-id",
1351
+ "preview_id": "your-kv-preview-id"
1352
+ }
1353
+ ],
1354
+ "durable_objects": {
1355
+ "bindings": [
1356
+ {
1357
+ "name": "ShardCoordinator",
1358
+ "class_name": "ShardCoordinator"
1359
+ }
1360
+ ]
1361
+ },
1362
+ "migrations": [
1363
+ {
1364
+ "tag": "v1",
1365
+ "new_sqlite_classes": ["ShardCoordinator"]
1366
+ }
1367
+ ]
1368
+ }
941
1369
  ```
942
1370
 
943
- #### Complete wrangler.toml with ShardCoordinator
1371
+ #### Complete wrangler.jsonc with ShardCoordinator
944
1372
 
945
- ```toml
946
- name = "collegedb-app"
947
- main = "src/index.ts"
948
- compatibility_date = "2024-08-10"
949
-
950
- # D1 Database bindings
951
- [[d1_databases]]
952
- binding = "db-east"
953
- database_name = "collegedb-east"
954
- database_id = "your-east-database-id"
955
-
956
- [[d1_databases]]
957
- binding = "db-west"
958
- database_name = "collegedb-west"
959
- database_id = "your-west-database-id"
960
-
961
- [[d1_databases]]
962
- binding = "db-central"
963
- database_name = "collegedb-central"
964
- database_id = "your-central-database-id"
965
-
966
- # KV namespace for shard mappings
967
- [[kv_namespaces]]
968
- binding = "KV"
969
- id = "your-kv-namespace-id"
970
- preview_id = "your-kv-preview-id" # For local development
971
-
972
- # Durable Object for shard coordination
973
- [[durable_objects.bindings]]
974
- name = "ShardCoordinator"
975
- class_name = "ShardCoordinator"
976
-
977
- # Environment-specific configurations
978
- [env.production]
979
- [[env.production.d1_databases]]
980
- binding = "db-east"
981
- database_name = "collegedb-prod-east"
982
- database_id = "your-prod-east-id"
983
-
984
- [[env.production.d1_databases]]
985
- binding = "db-west"
986
- database_name = "collegedb-prod-west"
987
- database_id = "your-prod-west-id"
988
-
989
- [[env.production.kv_namespaces]]
990
- binding = "KV"
991
- id = "your-prod-kv-namespace-id"
992
-
993
- [[env.production.durable_objects.bindings]]
994
- name = "ShardCoordinator"
995
- class_name = "ShardCoordinator"
1373
+ ```jsonc
1374
+ {
1375
+ "$schema": "./node_modules/wrangler/config-schema.json",
1376
+ "name": "collegedb-app",
1377
+ "main": "src/index.ts",
1378
+ "compatibility_date": "2026-04-15",
1379
+ "d1_databases": [
1380
+ {
1381
+ "binding": "db-east",
1382
+ "database_name": "collegedb-east",
1383
+ "database_id": "your-east-database-id"
1384
+ },
1385
+ {
1386
+ "binding": "db-west",
1387
+ "database_name": "collegedb-west",
1388
+ "database_id": "your-west-database-id"
1389
+ },
1390
+ {
1391
+ "binding": "db-central",
1392
+ "database_name": "collegedb-central",
1393
+ "database_id": "your-central-database-id"
1394
+ }
1395
+ ],
1396
+ "kv_namespaces": [
1397
+ {
1398
+ "binding": "KV",
1399
+ "id": "your-kv-namespace-id",
1400
+ "preview_id": "your-kv-preview-id"
1401
+ }
1402
+ ],
1403
+ "durable_objects": {
1404
+ "bindings": [
1405
+ {
1406
+ "name": "ShardCoordinator",
1407
+ "class_name": "ShardCoordinator"
1408
+ }
1409
+ ]
1410
+ },
1411
+ "migrations": [
1412
+ {
1413
+ "tag": "v1",
1414
+ "new_sqlite_classes": ["ShardCoordinator"]
1415
+ }
1416
+ ],
1417
+ "env": {
1418
+ "production": {
1419
+ "d1_databases": [
1420
+ {
1421
+ "binding": "db-east",
1422
+ "database_name": "collegedb-prod-east",
1423
+ "database_id": "your-prod-east-id"
1424
+ },
1425
+ {
1426
+ "binding": "db-west",
1427
+ "database_name": "collegedb-prod-west",
1428
+ "database_id": "your-prod-west-id"
1429
+ }
1430
+ ],
1431
+ "kv_namespaces": [
1432
+ {
1433
+ "binding": "KV",
1434
+ "id": "your-prod-kv-namespace-id"
1435
+ }
1436
+ ],
1437
+ "durable_objects": {
1438
+ "bindings": [
1439
+ {
1440
+ "name": "ShardCoordinator",
1441
+ "class_name": "ShardCoordinator"
1442
+ }
1443
+ ]
1444
+ }
1445
+ }
1446
+ }
1447
+ }
996
1448
  ```
997
1449
 
998
1450
  ### 3.1. Worker Script Setup (Required for ShardCoordinator)
@@ -1058,7 +1510,7 @@ wrangler deploy
1058
1510
  wrangler deploy --env production
1059
1511
  ```
1060
1512
 
1061
- ## 📊 Monitoring and Maintenance
1513
+ ## Monitoring and Maintenance
1062
1514
 
1063
1515
  ### Shard Statistics
1064
1516
 
@@ -1342,7 +1794,7 @@ export default {
1342
1794
  };
1343
1795
  ```
1344
1796
 
1345
- ## ⚙️ Performance Analysis
1797
+ ## Performance Analysis
1346
1798
 
1347
1799
  ### Scaling Performance Comparison
1348
1800
 
@@ -1732,7 +2184,7 @@ initialize({
1732
2184
  - Cost-sensitive deployments at small scale
1733
2185
  - **Single-strategy applications** where reads and writes have identical performance needs
1734
2186
 
1735
- ## �🔧 Advanced Configuration
2187
+ ## Advanced Configuration
1736
2188
 
1737
2189
  ### Custom Allocation Strategy
1738
2190
 
@@ -1762,19 +2214,25 @@ CollegeDB includes an optional **ShardCoordinator** Durable Object that provides
1762
2214
 
1763
2215
  #### Durable Object Setup
1764
2216
 
1765
- First, configure the Durable Object in your `wrangler.toml`:
2217
+ First, configure the Durable Object in your `wrangler.jsonc`:
1766
2218
 
1767
- ```toml
1768
- [[durable_objects.bindings]]
1769
- name = "ShardCoordinator"
1770
- class_name = "ShardCoordinator"
1771
-
1772
- # Export the ShardCoordinator class
1773
- [durable_objects.bindings.script_name]
1774
- # If using modules format
1775
- [[durable_objects.bindings]]
1776
- name = "ShardCoordinator"
1777
- class_name = "ShardCoordinator"
2219
+ ```jsonc
2220
+ {
2221
+ "durable_objects": {
2222
+ "bindings": [
2223
+ {
2224
+ "name": "ShardCoordinator",
2225
+ "class_name": "ShardCoordinator"
2226
+ }
2227
+ ]
2228
+ },
2229
+ "migrations": [
2230
+ {
2231
+ "tag": "v1",
2232
+ "new_sqlite_classes": ["ShardCoordinator"]
2233
+ }
2234
+ ]
2235
+ }
1778
2236
  ```
1779
2237
 
1780
2238
  #### Basic Usage with ShardCoordinator
@@ -2043,7 +2501,7 @@ async function allocateWithFallback(coordinator: DurableObjectNamespace, primary
2043
2501
  }
2044
2502
  ```
2045
2503
 
2046
- ## 🚀 Quick Reference
2504
+ ## Quick Reference
2047
2505
 
2048
2506
  ### Strategy Selection Guide
2049
2507
 
@@ -2161,7 +2619,7 @@ async function allocateWithFallback(coordinator: DurableObjectNamespace, primary
2161
2619
  | `me` | Middle East | Dubai |
2162
2620
  | `af` | Africa | Johannesburg |
2163
2621
 
2164
- ## 🤝 Contributing
2622
+ ## Contributing
2165
2623
 
2166
2624
  1. Fork the repository
2167
2625
  2. Create a feature branch: `git checkout -b feature/amazing-feature`
@@ -2169,18 +2627,18 @@ async function allocateWithFallback(coordinator: DurableObjectNamespace, primary
2169
2627
  4. Push to branch: `git push origin feature/amazing-feature`
2170
2628
  5. Submit a pull request
2171
2629
 
2172
- ## 📝 License
2630
+ ## License
2173
2631
 
2174
2632
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
2175
2633
 
2176
- ## 🔗 Links
2634
+ ## Links
2177
2635
 
2178
2636
  - [Cloudflare D1 Documentation](https://developers.cloudflare.com/d1/)
2179
2637
  - [Cloudflare KV Documentation](https://developers.cloudflare.com/kv/)
2180
2638
  - [Cloudflare Workers Documentation](https://developers.cloudflare.com/workers/)
2181
2639
  - [Durable Objects Documentation](https://developers.cloudflare.com/durable-objects/)
2182
2640
 
2183
- ## 🆘 Support
2641
+ ## Support
2184
2642
 
2185
2643
  - 📖 [Documentation](https://earth-app.github.io/CollegeDB)
2186
2644
  - 🐛 [Report Issues](https://github.com/earth-app/CollegeDB/issues)