@rocicorp/zero 1.4.0-canary.1 → 1.4.0-canary.2
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/out/analyze-query/src/bin-analyze.js +1 -1
- package/out/analyze-query/src/bin-analyze.js.map +1 -1
- package/out/analyze-query/src/bin-transform.js +1 -1
- package/out/analyze-query/src/bin-transform.js.map +1 -1
- package/out/replicache/src/btree/node.d.ts +1 -1
- package/out/replicache/src/btree/node.d.ts.map +1 -1
- package/out/replicache/src/btree/node.js +34 -21
- package/out/replicache/src/btree/node.js.map +1 -1
- package/out/replicache/src/btree/write.js +1 -2
- package/out/replicache/src/btree/write.js.map +1 -1
- package/out/shared/src/btree-set.d.ts +6 -0
- package/out/shared/src/btree-set.d.ts.map +1 -1
- package/out/shared/src/btree-set.js +34 -0
- package/out/shared/src/btree-set.js.map +1 -1
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +18 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +35 -3
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/scripts/decommission.d.ts.map +1 -1
- package/out/zero-cache/src/scripts/decommission.js +3 -3
- package/out/zero-cache/src/scripts/decommission.js.map +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +2 -5
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +6 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
- package/out/zero-cache/src/server/reaper.js +1 -4
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/shadow-syncer.js +35 -0
- package/out/zero-cache/src/server/shadow-syncer.js.map +1 -0
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +2 -8
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/server/worker-urls.d.ts +1 -0
- package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
- package/out/zero-cache/src/server/worker-urls.js +2 -1
- package/out/zero-cache/src/server/worker-urls.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +8 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +31 -18
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +44 -46
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +6 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +62 -22
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +2 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js +49 -0
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -0
- package/out/zero-cache/src/services/statz.js +3 -3
- package/out/zero-cache/src/services/statz.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +1 -0
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +34 -11
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +16 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +19 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +6 -0
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +45 -2
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-set-signature.d.ts +17 -0
- package/out/zero-cache/src/services/view-syncer/row-set-signature.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/row-set-signature.js +29 -0
- package/out/zero-cache/src/services/view-syncer/row-set-signature.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts +1 -0
- package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/cvr.js +1 -0
- package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/init.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/init.js +5 -1
- package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.d.ts +105 -0
- package/out/zero-cache/src/services/view-syncer/schema/types.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.js +8 -4
- package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +2 -2
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/pg.d.ts +1 -1
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +8 -2
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/types/timeout.d.ts +11 -0
- package/out/zero-cache/src/types/timeout.d.ts.map +1 -0
- package/out/zero-cache/src/types/timeout.js +26 -0
- package/out/zero-cache/src/types/timeout.js.map +1 -0
- package/out/zero-cache/src/workers/connection.js +3 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.js +1 -1
- package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +3 -3
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/query/query-internals.d.ts.map +1 -1
- package/out/zql/src/query/query-internals.js +1 -1
- package/out/zql/src/query/query-internals.js.map +1 -1
- package/package.json +1 -1
|
@@ -196,7 +196,7 @@ function runQuery(queryString) {
|
|
|
196
196
|
}, async () => {});
|
|
197
197
|
}
|
|
198
198
|
async function runHash(hash) {
|
|
199
|
-
const cvrDB = pgClient(lc, must(config.cvr.db, "CVR DB must be provided when using the hash option"));
|
|
199
|
+
const cvrDB = pgClient(lc, must(config.cvr.db, "CVR DB must be provided when using the hash option"), "analyze-query-hash-cvr");
|
|
200
200
|
const rows = await cvrDB`select "clientAST", "internal" from ${cvrDB(upstreamSchema(getShardID(config)) + "/cvr")}."queries" where "queryHash" = ${must(hash)} limit 1;`;
|
|
201
201
|
await cvrDB.end();
|
|
202
202
|
colorConsole.log("ZQL from Hash:");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin-analyze.js","names":[],"sources":["../../../../analyze-query/src/bin-analyze.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport fs from 'node:fs';\nimport {styleText} from 'node:util';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {logLevel, logOptions} from '../../otel/src/log-options.ts';\nimport {testLogConfig} from '../../otel/src/test-log-config.ts';\nimport {colorConsole, createLogContext} from '../../shared/src/logging.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {\n appOptions,\n shardOptions,\n ZERO_ENV_VAR_PREFIX,\n zeroOptions,\n} from '../../zero-cache/src/config/zero-config.ts';\nimport {\n computeZqlSpecs,\n mustGetTableSpec,\n} from '../../zero-cache/src/db/lite-tables.ts';\nimport {\n deployPermissionsOptions,\n loadSchemaAndPermissions,\n} from '../../zero-cache/src/scripts/permissions.ts';\nimport {runAst} from '../../zero-cache/src/services/run-ast.ts';\nimport {pgClient} from '../../zero-cache/src/types/pg.ts';\nimport {getShardID, upstreamSchema} from '../../zero-cache/src/types/shards.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport {type AST} from '../../zero-protocol/src/ast.ts';\nimport {clientSchemaFrom} from '../../zero-schema/src/builder/schema-builder.ts';\nimport {clientToServer} from '../../zero-schema/src/name-mapper.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {\n Debug,\n runtimeDebugFlags,\n} from '../../zql/src/builder/debug-delegate.ts';\nimport type {Source} from '../../zql/src/ivm/source.ts';\nimport {QueryDelegateBase} from '../../zql/src/query/query-delegate-base.ts';\nimport {newQuery} from '../../zql/src/query/query-impl.ts';\nimport {asQueryInternals} from '../../zql/src/query/query-internals.ts';\nimport type {PullRow, Query} from '../../zql/src/query/query.ts';\nimport {Database} from '../../zqlite/src/db.ts';\nimport {TableSource} from '../../zqlite/src/table-source.ts';\nimport {explainQueries} from './explain-queries.ts';\n\nconst options = {\n schema: deployPermissionsOptions.schema,\n replicaFile: {\n ...zeroOptions.replica.file,\n desc: [`File path to the SQLite replica to test queries against.`],\n },\n ast: {\n type: v.string().optional(),\n desc: [\n 'AST for the query to be analyzed. Only one of ast/query/hash should be provided.',\n ],\n },\n query: {\n type: v.string().optional(),\n desc: [\n `Query to be analyzed in the form of: table.where(...).related(...).etc. `,\n `Only one of ast/query/hash should be provided.`,\n ],\n },\n hash: {\n type: v.string().optional(),\n desc: [\n `Hash of the query to be analyzed. This is used to look up the query in the database. `,\n `Only one of ast/query/hash should be provided.`,\n `You should run this script from the directory containing your .env file to reduce the amount of`,\n `configuration required. The .env file should contain the connection URL to the CVR database.`,\n ],\n },\n applyPermissions: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to apply permissions (from your schema file) to the provided query.',\n ],\n },\n authData: {\n type: v.string().optional(),\n desc: [\n 'JSON encoded payload of the auth data.',\n 'This will be used to fill permission variables if the \"applyPermissions\" option is set',\n ],\n },\n outputVendedRows: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to output the rows which were read from the replica in order to execute the analyzed query. ',\n 'If the same row is read more than once it will be logged once for each time it was read.',\n ],\n },\n outputSyncedRows: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to output the rows which would be synced to the client for the analyzed query.',\n ],\n },\n cvr: {\n db: {\n type: v.string().optional(),\n desc: [\n 'Connection URL to the CVR database. If using --hash, either this or --upstream-db',\n 'must be specified.',\n ],\n },\n },\n upstream: {\n db: {\n desc: [\n `Connection URL to the \"upstream\" authoritative postgres database. If using --hash, `,\n 'either this or --cvr-db must be specified.',\n ],\n type: v.string().optional(),\n },\n type: zeroOptions.upstream.type,\n },\n app: appOptions,\n shard: shardOptions,\n log: {\n ...logOptions,\n level: logLevel.default('error'),\n },\n};\n\nconst cfg = parseOptions(options, {\n // the command line parses drops all text after the first newline\n // so we need to replace newlines with spaces\n // before parsing\n argv: process.argv.slice(2).map(s => s.replaceAll('\\n', ' ')),\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n description: [\n {\n header: 'analyze-query',\n content: `Analyze a ZQL query and show information about how it runs against a SQLite replica.\n\n analyze-query uses the same environment variables and flags as zero-cache-dev. If run from your development environment, it will pick up your ZERO_REPLICA_FILE, ZERO_SCHEMA_PATH, and other env vars automatically.\n\n If run in another environment (e.g., production) you will have to specify these flags. In particular, you must have a copy of the appropriate Zero schema file to give to the --schema-path flag.`,\n },\n {\n header: 'Examples',\n content: `# In development\n npx analyze-query --query='issue.related(\"comments\").limit(10)'\n npx analyze-query --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n npx analyze-query --hash=1234567890\n\n # In production\n # First copy schema.ts to your production environment, then run:\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n\n # cvr-db is required when using the hash option.\n # It is typically the same as your upstream db.\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --cvr-db='postgres://user:pass@host:port/db' \\\\\n --hash=1234567890\n `,\n },\n ],\n});\nconst config = {\n ...cfg,\n cvr: {\n ...cfg.cvr,\n db: cfg.cvr.db ?? cfg.upstream.db,\n },\n};\n\nruntimeDebugFlags.trackRowCountsVended = true;\nruntimeDebugFlags.trackRowsVended = config.outputVendedRows;\n\nconst lc = createLogContext({\n log: config.log,\n});\n\nif (!fs.existsSync(config.replicaFile)) {\n colorConsole.error(`Replica file ${config.replicaFile} does not exist`);\n process.exit(1);\n}\nconst db = new Database(lc, config.replicaFile);\n\nconst {schema, permissions} = await loadSchemaAndPermissions(\n config.schema.path,\n);\nconst clientSchema = clientSchemaFrom(schema).clientSchema;\n\nconst sources = new Map<string, TableSource>();\nconst clientToServerMapper = clientToServer(schema.tables);\nconst debug = new Debug();\nconst tableSpecs = computeZqlSpecs(lc, db, {includeBackfillingColumns: false});\n\nclass AnalyzeQueryDelegate extends QueryDelegateBase {\n readonly debug = debug;\n readonly defaultQueryComplete = true;\n\n getSource(serverTableName: string): Source | undefined {\n let source = sources.get(serverTableName);\n if (source) {\n return source;\n }\n const tableSpec = mustGetTableSpec(tableSpecs, serverTableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n testLogConfig,\n db,\n serverTableName,\n tableSpec.zqlSpec,\n primaryKey,\n );\n\n sources.set(serverTableName, source);\n return source;\n }\n}\n\nconst host = new AnalyzeQueryDelegate();\n\nlet result: AnalyzeQueryResult;\n\nif (config.ast) {\n // the user likely has a transformed AST since the wire and storage formats are the transformed AST\n result = await runAst(\n lc,\n clientSchema,\n JSON.parse(config.ast),\n true,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n} else if (config.query) {\n result = await runQuery(config.query);\n} else if (config.hash) {\n result = await runHash(config.hash);\n} else {\n colorConsole.error('No query or AST or hash provided');\n process.exit(1);\n}\n\nfunction runQuery(queryString: string): Promise<AnalyzeQueryResult> {\n const z = {\n query: Object.fromEntries(\n Object.entries(schema.tables).map(([name]) => [\n name,\n newQuery(schema, name),\n ]),\n ),\n };\n\n const f = new Function('z', `return z.query.${queryString};`);\n const q: Query<string, Schema, PullRow<string, Schema>> = f(z);\n\n const ast = asQueryInternals(q).ast;\n return runAst(\n lc,\n clientSchema,\n ast,\n false,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n}\n\nasync function runHash(hash: string) {\n const cvrDB = pgClient(\n lc,\n must(config.cvr.db, 'CVR DB must be provided when using the hash option'),\n );\n\n const rows = await cvrDB`select \"clientAST\", \"internal\" from ${cvrDB(\n upstreamSchema(getShardID(config)) + '/cvr',\n )}.\"queries\" where \"queryHash\" = ${must(hash)} limit 1;`;\n await cvrDB.end();\n\n colorConsole.log('ZQL from Hash:');\n const ast = rows[0].clientAST as AST;\n colorConsole.log(await formatOutput(ast.table + astToZQL(ast)));\n\n return runAst(\n lc,\n clientSchema,\n ast,\n true,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n}\n\nif (config.outputSyncedRows) {\n colorConsole.log(styleText(['blue', 'bold'], '=== Synced Rows: ===\\n'));\n for (const [table, rows] of Object.entries(result.syncedRows ?? {})) {\n colorConsole.log(styleText('bold', table + ':'), rows);\n }\n}\n\ncolorConsole.log(styleText(['blue', 'bold'], '=== Query Stats: ===\\n'));\ncolorConsole.log(\n styleText('bold', 'total synced rows:'),\n result.syncedRowCount,\n);\nshowStats();\nif (config.outputVendedRows) {\n colorConsole.log(\n styleText(['blue', 'bold'], '=== JS Row Scan Values: ===\\n'),\n );\n for (const source of sources.values()) {\n colorConsole.log(\n styleText('bold', `${source.tableSchema.name}:`),\n debug.getVendedRows()?.[source.tableSchema.name] ?? {},\n );\n }\n}\n\ncolorConsole.log(\n styleText(['blue', 'bold'], '\\n=== Rows Scanned (by SQLite): ===\\n'),\n);\nconst nvisitCounts = debug.getNVisitCounts();\nlet totalNVisit = 0;\nfor (const [table, queries] of Object.entries(nvisitCounts)) {\n colorConsole.log(styleText('bold', `${table}:`), queries);\n for (const count of Object.values(queries)) {\n totalNVisit += count;\n }\n}\ncolorConsole.log(\n styleText('bold', 'total rows scanned:'),\n colorRowsConsidered(totalNVisit),\n);\n\ncolorConsole.log(styleText(['blue', 'bold'], '\\n\\n=== Query Plans: ===\\n'));\nconst plans = explainQueries(debug.getVendedRowCounts() ?? {}, db);\nfor (const [query, plan] of Object.entries(plans)) {\n colorConsole.log(styleText('bold', 'query'), query);\n colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join('\\n'));\n colorConsole.log('\\n');\n}\n\nfunction showStats() {\n let totalRowsConsidered = 0;\n for (const source of sources.values()) {\n const values = Object.values(\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n for (const v of values) {\n totalRowsConsidered += v;\n }\n colorConsole.log(\n styleText('bold', source.tableSchema.name + ' vended:'),\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n }\n\n colorConsole.log(\n styleText('bold', 'Rows Read (into JS):'),\n colorRowsConsidered(totalRowsConsidered),\n );\n colorConsole.log(\n styleText('bold', 'time:'),\n colorTime(result.end - result.start),\n 'ms',\n );\n}\n\nfunction colorTime(duration: number) {\n if (duration < 100) {\n return styleText('green', duration.toFixed(2) + 'ms');\n } else if (duration < 1000) {\n return styleText('yellow', duration.toFixed(2) + 'ms');\n }\n return styleText('red', duration.toFixed(2) + 'ms');\n}\n\nfunction colorRowsConsidered(n: number) {\n if (n < 1000) {\n return styleText('green', n.toString());\n } else if (n < 10000) {\n return styleText('yellow', n.toString());\n }\n return styleText('red', n.toString());\n}\n\nfunction colorPlanRow(row: string, i: number) {\n if (row.includes('SCAN')) {\n if (i === 0) {\n return styleText('yellow', row);\n }\n return styleText('red', row);\n }\n return styleText('green', row);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bin-analyze.js","names":[],"sources":["../../../../analyze-query/src/bin-analyze.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport fs from 'node:fs';\nimport {styleText} from 'node:util';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {logLevel, logOptions} from '../../otel/src/log-options.ts';\nimport {testLogConfig} from '../../otel/src/test-log-config.ts';\nimport {colorConsole, createLogContext} from '../../shared/src/logging.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {\n appOptions,\n shardOptions,\n ZERO_ENV_VAR_PREFIX,\n zeroOptions,\n} from '../../zero-cache/src/config/zero-config.ts';\nimport {\n computeZqlSpecs,\n mustGetTableSpec,\n} from '../../zero-cache/src/db/lite-tables.ts';\nimport {\n deployPermissionsOptions,\n loadSchemaAndPermissions,\n} from '../../zero-cache/src/scripts/permissions.ts';\nimport {runAst} from '../../zero-cache/src/services/run-ast.ts';\nimport {pgClient} from '../../zero-cache/src/types/pg.ts';\nimport {getShardID, upstreamSchema} from '../../zero-cache/src/types/shards.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport {type AST} from '../../zero-protocol/src/ast.ts';\nimport {clientSchemaFrom} from '../../zero-schema/src/builder/schema-builder.ts';\nimport {clientToServer} from '../../zero-schema/src/name-mapper.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {\n Debug,\n runtimeDebugFlags,\n} from '../../zql/src/builder/debug-delegate.ts';\nimport type {Source} from '../../zql/src/ivm/source.ts';\nimport {QueryDelegateBase} from '../../zql/src/query/query-delegate-base.ts';\nimport {newQuery} from '../../zql/src/query/query-impl.ts';\nimport {asQueryInternals} from '../../zql/src/query/query-internals.ts';\nimport type {PullRow, Query} from '../../zql/src/query/query.ts';\nimport {Database} from '../../zqlite/src/db.ts';\nimport {TableSource} from '../../zqlite/src/table-source.ts';\nimport {explainQueries} from './explain-queries.ts';\n\nconst options = {\n schema: deployPermissionsOptions.schema,\n replicaFile: {\n ...zeroOptions.replica.file,\n desc: [`File path to the SQLite replica to test queries against.`],\n },\n ast: {\n type: v.string().optional(),\n desc: [\n 'AST for the query to be analyzed. Only one of ast/query/hash should be provided.',\n ],\n },\n query: {\n type: v.string().optional(),\n desc: [\n `Query to be analyzed in the form of: table.where(...).related(...).etc. `,\n `Only one of ast/query/hash should be provided.`,\n ],\n },\n hash: {\n type: v.string().optional(),\n desc: [\n `Hash of the query to be analyzed. This is used to look up the query in the database. `,\n `Only one of ast/query/hash should be provided.`,\n `You should run this script from the directory containing your .env file to reduce the amount of`,\n `configuration required. The .env file should contain the connection URL to the CVR database.`,\n ],\n },\n applyPermissions: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to apply permissions (from your schema file) to the provided query.',\n ],\n },\n authData: {\n type: v.string().optional(),\n desc: [\n 'JSON encoded payload of the auth data.',\n 'This will be used to fill permission variables if the \"applyPermissions\" option is set',\n ],\n },\n outputVendedRows: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to output the rows which were read from the replica in order to execute the analyzed query. ',\n 'If the same row is read more than once it will be logged once for each time it was read.',\n ],\n },\n outputSyncedRows: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to output the rows which would be synced to the client for the analyzed query.',\n ],\n },\n cvr: {\n db: {\n type: v.string().optional(),\n desc: [\n 'Connection URL to the CVR database. If using --hash, either this or --upstream-db',\n 'must be specified.',\n ],\n },\n },\n upstream: {\n db: {\n desc: [\n `Connection URL to the \"upstream\" authoritative postgres database. If using --hash, `,\n 'either this or --cvr-db must be specified.',\n ],\n type: v.string().optional(),\n },\n type: zeroOptions.upstream.type,\n },\n app: appOptions,\n shard: shardOptions,\n log: {\n ...logOptions,\n level: logLevel.default('error'),\n },\n};\n\nconst cfg = parseOptions(options, {\n // the command line parses drops all text after the first newline\n // so we need to replace newlines with spaces\n // before parsing\n argv: process.argv.slice(2).map(s => s.replaceAll('\\n', ' ')),\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n description: [\n {\n header: 'analyze-query',\n content: `Analyze a ZQL query and show information about how it runs against a SQLite replica.\n\n analyze-query uses the same environment variables and flags as zero-cache-dev. If run from your development environment, it will pick up your ZERO_REPLICA_FILE, ZERO_SCHEMA_PATH, and other env vars automatically.\n\n If run in another environment (e.g., production) you will have to specify these flags. In particular, you must have a copy of the appropriate Zero schema file to give to the --schema-path flag.`,\n },\n {\n header: 'Examples',\n content: `# In development\n npx analyze-query --query='issue.related(\"comments\").limit(10)'\n npx analyze-query --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n npx analyze-query --hash=1234567890\n\n # In production\n # First copy schema.ts to your production environment, then run:\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n\n # cvr-db is required when using the hash option.\n # It is typically the same as your upstream db.\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --cvr-db='postgres://user:pass@host:port/db' \\\\\n --hash=1234567890\n `,\n },\n ],\n});\nconst config = {\n ...cfg,\n cvr: {\n ...cfg.cvr,\n db: cfg.cvr.db ?? cfg.upstream.db,\n },\n};\n\nruntimeDebugFlags.trackRowCountsVended = true;\nruntimeDebugFlags.trackRowsVended = config.outputVendedRows;\n\nconst lc = createLogContext({\n log: config.log,\n});\n\nif (!fs.existsSync(config.replicaFile)) {\n colorConsole.error(`Replica file ${config.replicaFile} does not exist`);\n process.exit(1);\n}\nconst db = new Database(lc, config.replicaFile);\n\nconst {schema, permissions} = await loadSchemaAndPermissions(\n config.schema.path,\n);\nconst clientSchema = clientSchemaFrom(schema).clientSchema;\n\nconst sources = new Map<string, TableSource>();\nconst clientToServerMapper = clientToServer(schema.tables);\nconst debug = new Debug();\nconst tableSpecs = computeZqlSpecs(lc, db, {includeBackfillingColumns: false});\n\nclass AnalyzeQueryDelegate extends QueryDelegateBase {\n readonly debug = debug;\n readonly defaultQueryComplete = true;\n\n getSource(serverTableName: string): Source | undefined {\n let source = sources.get(serverTableName);\n if (source) {\n return source;\n }\n const tableSpec = mustGetTableSpec(tableSpecs, serverTableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n testLogConfig,\n db,\n serverTableName,\n tableSpec.zqlSpec,\n primaryKey,\n );\n\n sources.set(serverTableName, source);\n return source;\n }\n}\n\nconst host = new AnalyzeQueryDelegate();\n\nlet result: AnalyzeQueryResult;\n\nif (config.ast) {\n // the user likely has a transformed AST since the wire and storage formats are the transformed AST\n result = await runAst(\n lc,\n clientSchema,\n JSON.parse(config.ast),\n true,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n} else if (config.query) {\n result = await runQuery(config.query);\n} else if (config.hash) {\n result = await runHash(config.hash);\n} else {\n colorConsole.error('No query or AST or hash provided');\n process.exit(1);\n}\n\nfunction runQuery(queryString: string): Promise<AnalyzeQueryResult> {\n const z = {\n query: Object.fromEntries(\n Object.entries(schema.tables).map(([name]) => [\n name,\n newQuery(schema, name),\n ]),\n ),\n };\n\n const f = new Function('z', `return z.query.${queryString};`);\n const q: Query<string, Schema, PullRow<string, Schema>> = f(z);\n\n const ast = asQueryInternals(q).ast;\n return runAst(\n lc,\n clientSchema,\n ast,\n false,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n}\n\nasync function runHash(hash: string) {\n const cvrDB = pgClient(\n lc,\n must(config.cvr.db, 'CVR DB must be provided when using the hash option'),\n 'analyze-query-hash-cvr',\n );\n\n const rows = await cvrDB`select \"clientAST\", \"internal\" from ${cvrDB(\n upstreamSchema(getShardID(config)) + '/cvr',\n )}.\"queries\" where \"queryHash\" = ${must(hash)} limit 1;`;\n await cvrDB.end();\n\n colorConsole.log('ZQL from Hash:');\n const ast = rows[0].clientAST as AST;\n colorConsole.log(await formatOutput(ast.table + astToZQL(ast)));\n\n return runAst(\n lc,\n clientSchema,\n ast,\n true,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n}\n\nif (config.outputSyncedRows) {\n colorConsole.log(styleText(['blue', 'bold'], '=== Synced Rows: ===\\n'));\n for (const [table, rows] of Object.entries(result.syncedRows ?? {})) {\n colorConsole.log(styleText('bold', table + ':'), rows);\n }\n}\n\ncolorConsole.log(styleText(['blue', 'bold'], '=== Query Stats: ===\\n'));\ncolorConsole.log(\n styleText('bold', 'total synced rows:'),\n result.syncedRowCount,\n);\nshowStats();\nif (config.outputVendedRows) {\n colorConsole.log(\n styleText(['blue', 'bold'], '=== JS Row Scan Values: ===\\n'),\n );\n for (const source of sources.values()) {\n colorConsole.log(\n styleText('bold', `${source.tableSchema.name}:`),\n debug.getVendedRows()?.[source.tableSchema.name] ?? {},\n );\n }\n}\n\ncolorConsole.log(\n styleText(['blue', 'bold'], '\\n=== Rows Scanned (by SQLite): ===\\n'),\n);\nconst nvisitCounts = debug.getNVisitCounts();\nlet totalNVisit = 0;\nfor (const [table, queries] of Object.entries(nvisitCounts)) {\n colorConsole.log(styleText('bold', `${table}:`), queries);\n for (const count of Object.values(queries)) {\n totalNVisit += count;\n }\n}\ncolorConsole.log(\n styleText('bold', 'total rows scanned:'),\n colorRowsConsidered(totalNVisit),\n);\n\ncolorConsole.log(styleText(['blue', 'bold'], '\\n\\n=== Query Plans: ===\\n'));\nconst plans = explainQueries(debug.getVendedRowCounts() ?? {}, db);\nfor (const [query, plan] of Object.entries(plans)) {\n colorConsole.log(styleText('bold', 'query'), query);\n colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join('\\n'));\n colorConsole.log('\\n');\n}\n\nfunction showStats() {\n let totalRowsConsidered = 0;\n for (const source of sources.values()) {\n const values = Object.values(\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n for (const v of values) {\n totalRowsConsidered += v;\n }\n colorConsole.log(\n styleText('bold', source.tableSchema.name + ' vended:'),\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n }\n\n colorConsole.log(\n styleText('bold', 'Rows Read (into JS):'),\n colorRowsConsidered(totalRowsConsidered),\n );\n colorConsole.log(\n styleText('bold', 'time:'),\n colorTime(result.end - result.start),\n 'ms',\n );\n}\n\nfunction colorTime(duration: number) {\n if (duration < 100) {\n return styleText('green', duration.toFixed(2) + 'ms');\n } else if (duration < 1000) {\n return styleText('yellow', duration.toFixed(2) + 'ms');\n }\n return styleText('red', duration.toFixed(2) + 'ms');\n}\n\nfunction colorRowsConsidered(n: number) {\n if (n < 1000) {\n return styleText('green', n.toString());\n } else if (n < 10000) {\n return styleText('yellow', n.toString());\n }\n return styleText('red', n.toString());\n}\n\nfunction colorPlanRow(row: string, i: number) {\n if (row.includes('SCAN')) {\n if (i === 0) {\n return styleText('yellow', row);\n }\n return styleText('red', row);\n }\n return styleText('green', row);\n}\n"],"mappings":"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"}
|
|
@@ -26,7 +26,7 @@ var config = parseOptions({
|
|
|
26
26
|
}, { envNamePrefix: ZERO_ENV_VAR_PREFIX });
|
|
27
27
|
var lc = new LogContext("debug", {}, consoleLogSink);
|
|
28
28
|
var { permissions } = await loadSchemaAndPermissions(config.schema);
|
|
29
|
-
var cvrDB = pgClient(lc, config.cvr.db);
|
|
29
|
+
var cvrDB = pgClient(lc, config.cvr.db, "analyze-query-transform-cvr");
|
|
30
30
|
var rows = await cvrDB`select "clientAST", "internal" from ${cvrDB(upstreamSchema(getShardID(config)) + "/cvr")}."queries" where "queryHash" = ${must(config.hash)} limit 1;`;
|
|
31
31
|
var queryAst = transformAndHashQuery(lc, "", rows[0].clientAST, permissions, {
|
|
32
32
|
type: "jwt",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin-transform.js","names":[],"sources":["../../../../analyze-query/src/bin-transform.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {transformAndHashQuery} from '../../zero-cache/src/auth/read-authorizer.ts';\nimport {\n appOptions,\n shardOptions,\n ZERO_ENV_VAR_PREFIX,\n} from '../../zero-cache/src/config/zero-config.ts';\nimport {loadSchemaAndPermissions} from '../../zero-cache/src/scripts/permissions.ts';\nimport {pgClient} from '../../zero-cache/src/types/pg.ts';\nimport {getShardID, upstreamSchema} from '../../zero-cache/src/types/shards.ts';\n\nconst options = {\n cvr: {db: v.string()},\n schema: {\n type: v.string().default('./schema.ts'),\n desc: ['Path to the schema file.'],\n },\n app: appOptions,\n shard: shardOptions,\n hash: {\n type: v.string().optional(),\n desc: ['Hash of the query to fetch the AST for.'],\n },\n};\n\nconst config = parseOptions(options, {envNamePrefix: ZERO_ENV_VAR_PREFIX});\n\nconst lc = new LogContext('debug', {}, consoleLogSink);\nconst {permissions} = await loadSchemaAndPermissions(config.schema);\n\nconst cvrDB = pgClient(lc, config.cvr.db);\n\nconst rows =\n await cvrDB`select \"clientAST\", \"internal\" from ${cvrDB(upstreamSchema(getShardID(config)) + '/cvr')}.\"queries\" where \"queryHash\" = ${must(\n config.hash,\n )} limit 1;`;\n\nconst queryAst = transformAndHashQuery(\n lc,\n '',\n rows[0].clientAST,\n permissions,\n {\n type: 'jwt',\n raw: '',\n decoded: {},\n },\n rows[0].internal,\n).transformedAst;\n\n// oxlint-disable no-console\nconsole.log('\\n=== AST ===\\n');\nconsole.log(JSON.stringify(queryAst, null, 2));\nconsole.log('\\n=== ZQL ===\\n');\nconsole.log(await formatOutput(queryAst.table + astToZQL(queryAst)));\n// oxlint-enable no-console\n\nawait cvrDB.end();\n"],"mappings":";;;;;;;;;;;;;AAgCA,IAAM,SAAS,aAdC;CACd,KAAK,EAAC,IAAI,eAAE,QAAQ,EAAC;CACrB,QAAQ;EACN,MAAM,eAAE,QAAQ,CAAC,QAAQ,cAAc;EACvC,MAAM,CAAC,2BAA2B;EACnC;CACD,KAAK;CACL,OAAO;CACP,MAAM;EACJ,MAAM,eAAE,QAAQ,CAAC,UAAU;EAC3B,MAAM,CAAC,0CAA0C;EAClD;CACF,EAEoC,EAAC,eAAe,qBAAoB,CAAC;AAE1E,IAAM,KAAK,IAAI,WAAW,SAAS,EAAE,EAAE,eAAe;AACtD,IAAM,EAAC,gBAAe,MAAM,yBAAyB,OAAO,OAAO;AAEnE,IAAM,QAAQ,SAAS,IAAI,OAAO,IAAI,
|
|
1
|
+
{"version":3,"file":"bin-transform.js","names":[],"sources":["../../../../analyze-query/src/bin-transform.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {transformAndHashQuery} from '../../zero-cache/src/auth/read-authorizer.ts';\nimport {\n appOptions,\n shardOptions,\n ZERO_ENV_VAR_PREFIX,\n} from '../../zero-cache/src/config/zero-config.ts';\nimport {loadSchemaAndPermissions} from '../../zero-cache/src/scripts/permissions.ts';\nimport {pgClient} from '../../zero-cache/src/types/pg.ts';\nimport {getShardID, upstreamSchema} from '../../zero-cache/src/types/shards.ts';\n\nconst options = {\n cvr: {db: v.string()},\n schema: {\n type: v.string().default('./schema.ts'),\n desc: ['Path to the schema file.'],\n },\n app: appOptions,\n shard: shardOptions,\n hash: {\n type: v.string().optional(),\n desc: ['Hash of the query to fetch the AST for.'],\n },\n};\n\nconst config = parseOptions(options, {envNamePrefix: ZERO_ENV_VAR_PREFIX});\n\nconst lc = new LogContext('debug', {}, consoleLogSink);\nconst {permissions} = await loadSchemaAndPermissions(config.schema);\n\nconst cvrDB = pgClient(lc, config.cvr.db, 'analyze-query-transform-cvr');\n\nconst rows =\n await cvrDB`select \"clientAST\", \"internal\" from ${cvrDB(upstreamSchema(getShardID(config)) + '/cvr')}.\"queries\" where \"queryHash\" = ${must(\n config.hash,\n )} limit 1;`;\n\nconst queryAst = transformAndHashQuery(\n lc,\n '',\n rows[0].clientAST,\n permissions,\n {\n type: 'jwt',\n raw: '',\n decoded: {},\n },\n rows[0].internal,\n).transformedAst;\n\n// oxlint-disable no-console\nconsole.log('\\n=== AST ===\\n');\nconsole.log(JSON.stringify(queryAst, null, 2));\nconsole.log('\\n=== ZQL ===\\n');\nconsole.log(await formatOutput(queryAst.table + astToZQL(queryAst)));\n// oxlint-enable no-console\n\nawait cvrDB.end();\n"],"mappings":";;;;;;;;;;;;;AAgCA,IAAM,SAAS,aAdC;CACd,KAAK,EAAC,IAAI,eAAE,QAAQ,EAAC;CACrB,QAAQ;EACN,MAAM,eAAE,QAAQ,CAAC,QAAQ,cAAc;EACvC,MAAM,CAAC,2BAA2B;EACnC;CACD,KAAK;CACL,OAAO;CACP,MAAM;EACJ,MAAM,eAAE,QAAQ,CAAC,UAAU;EAC3B,MAAM,CAAC,0CAA0C;EAClD;CACF,EAEoC,EAAC,eAAe,qBAAoB,CAAC;AAE1E,IAAM,KAAK,IAAI,WAAW,SAAS,EAAE,EAAE,eAAe;AACtD,IAAM,EAAC,gBAAe,MAAM,yBAAyB,OAAO,OAAO;AAEnE,IAAM,QAAQ,SAAS,IAAI,OAAO,IAAI,IAAI,8BAA8B;AAExE,IAAM,OACJ,MAAM,KAAK,uCAAuC,MAAM,eAAe,WAAW,OAAO,CAAC,GAAG,OAAO,CAAC,iCAAiC,KACpI,OAAO,KACR,CAAC;AAEJ,IAAM,WAAW,sBACf,IACA,IACA,KAAK,GAAG,WACR,aACA;CACE,MAAM;CACN,KAAK;CACL,SAAS,EAAE;CACZ,EACD,KAAK,GAAG,SACT,CAAC;AAGF,QAAQ,IAAI,kBAAkB;AAC9B,QAAQ,IAAI,KAAK,UAAU,UAAU,MAAM,EAAE,CAAC;AAC9C,QAAQ,IAAI,kBAAkB;AAC9B,QAAQ,IAAI,MAAM,aAAa,SAAS,QAAQ,SAAS,SAAS,CAAC,CAAC;AAGpE,MAAM,MAAM,KAAK"}
|
|
@@ -119,7 +119,7 @@ export declare function newNodeImpl(entries: Array<Entry<FrozenJSONValue>>, hash
|
|
|
119
119
|
export declare function newNodeImpl(entries: Array<Entry<Hash>>, hash: Hash, level: number, isMutable: boolean): InternalNodeImpl;
|
|
120
120
|
export declare function newNodeImpl(entries: Array<Entry<FrozenJSONValue>> | Array<Entry<Hash>>, hash: Hash, level: number, isMutable: boolean): DataNodeImpl | InternalNodeImpl;
|
|
121
121
|
export declare function isDataNodeImpl(node: DataNodeImpl | InternalNodeImpl): node is DataNodeImpl;
|
|
122
|
-
export declare function partition<T>(values: Iterable<T>, getSizeOfEntry: (v: T) => number, min: number, max: number
|
|
122
|
+
export declare function partition<T, R>(values: Iterable<T>, getSizeOfEntry: (v: T) => number, min: number, max: number, create: (entries: T[]) => R): R[];
|
|
123
123
|
export declare const emptyDataNode: BaseNode<ReadonlyJSONValue>;
|
|
124
124
|
export declare const emptyDataNodeImpl: DataNodeImpl;
|
|
125
125
|
export declare function createNewInternalEntryForNode(node: NodeImpl<unknown>, getSizeOfEntry: <K, V>(k: K, v: V) => number): [string, Hash, number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/btree/node.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/btree/node.ts"],"names":[],"mappings":"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"}
|
|
@@ -186,12 +186,9 @@ var InternalNodeImpl = class InternalNodeImpl extends NodeImpl {
|
|
|
186
186
|
startIndex = i;
|
|
187
187
|
removeCount = 1;
|
|
188
188
|
}
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const newHashEntry = createNewInternalEntryForNode(tree.newNodeImpl(entries, level), tree.getEntrySize);
|
|
193
|
-
newEntries.push(newHashEntry);
|
|
194
|
-
}
|
|
189
|
+
const newEntries = partition(values, (value) => value[2], tree.minSize - tree.chunkHeaderSize, tree.maxSize - tree.chunkHeaderSize, (entries) => {
|
|
190
|
+
return createNewInternalEntryForNode(tree.newNodeImpl(entries, level), tree.getEntrySize);
|
|
191
|
+
});
|
|
195
192
|
if (this.isMutable) {
|
|
196
193
|
this.entries.splice(startIndex, removeCount, ...newEntries);
|
|
197
194
|
this._updateNode(tree);
|
|
@@ -261,36 +258,52 @@ function newNodeImpl(entries, hash, level, isMutable) {
|
|
|
261
258
|
function isDataNodeImpl(node) {
|
|
262
259
|
return node.level === 0;
|
|
263
260
|
}
|
|
264
|
-
function partition(values, getSizeOfEntry, min, max) {
|
|
265
|
-
const
|
|
266
|
-
const sizes = [];
|
|
261
|
+
function partition(values, getSizeOfEntry, min, max, create) {
|
|
262
|
+
const results = [];
|
|
267
263
|
let sum = 0;
|
|
268
264
|
let accum = [];
|
|
265
|
+
let lastPartition = null;
|
|
266
|
+
let lastSize = 0;
|
|
267
|
+
function commitLast() {
|
|
268
|
+
if (lastPartition !== null) {
|
|
269
|
+
results.push(create(lastPartition));
|
|
270
|
+
lastPartition = null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
269
273
|
for (const value of values) {
|
|
270
274
|
const size = getSizeOfEntry(value);
|
|
271
275
|
if (size >= max) {
|
|
272
276
|
if (accum.length > 0) {
|
|
273
|
-
|
|
274
|
-
|
|
277
|
+
commitLast();
|
|
278
|
+
lastPartition = accum;
|
|
279
|
+
lastSize = sum;
|
|
280
|
+
accum = [];
|
|
281
|
+
sum = 0;
|
|
275
282
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
accum = [];
|
|
283
|
+
commitLast();
|
|
284
|
+
lastPartition = [value];
|
|
285
|
+
lastSize = size;
|
|
280
286
|
} else if (sum + size >= min) {
|
|
281
287
|
accum.push(value);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
288
|
+
commitLast();
|
|
289
|
+
lastPartition = accum;
|
|
290
|
+
lastSize = sum + size;
|
|
285
291
|
accum = [];
|
|
292
|
+
sum = 0;
|
|
286
293
|
} else {
|
|
287
294
|
sum += size;
|
|
288
295
|
accum.push(value);
|
|
289
296
|
}
|
|
290
297
|
}
|
|
291
|
-
if (sum > 0) if (
|
|
292
|
-
|
|
293
|
-
|
|
298
|
+
if (sum > 0) if (lastPartition !== null && sum + lastSize <= max) {
|
|
299
|
+
lastPartition.push(...accum);
|
|
300
|
+
commitLast();
|
|
301
|
+
} else {
|
|
302
|
+
commitLast();
|
|
303
|
+
results.push(create(accum));
|
|
304
|
+
}
|
|
305
|
+
else commitLast();
|
|
306
|
+
return results;
|
|
294
307
|
}
|
|
295
308
|
var emptyDataNode = makeNodeChunkData(0, [], 7);
|
|
296
309
|
var emptyDataNodeImpl = new DataNodeImpl([], emptyHash, false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.js","names":["#childNodeSize","#splice","#mergeAndPartition","#replaceChild"],"sources":["../../../../../replicache/src/btree/node.ts"],"sourcesContent":["import {compareUTF8} from 'compare-utf8';\nimport {\n assert,\n assertArray,\n assertNumber,\n assertString,\n} from '../../../shared/src/asserts.ts';\nimport {binarySearch as binarySearchWithFunc} from '../../../shared/src/binary-search.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport {joinIterables} from '../../../shared/src/iterables.ts';\nimport {\n type JSONValue,\n type ReadonlyJSONValue,\n assertJSONValue,\n} from '../../../shared/src/json.ts';\nimport {skipBTreeNodeAsserts} from '../config.ts';\nimport type {IndexKey} from '../db/index.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport {\n type FrozenJSONValue,\n type FrozenTag,\n assertDeepFrozen,\n deepFreeze,\n} from '../frozen-json.ts';\nimport {type Hash, emptyHash, newRandomHash} from '../hash.ts';\nimport type {BTreeRead} from './read.ts';\nimport type {BTreeWrite} from './write.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport type Entry<V> = readonly [key: string, value: V, sizeOfEntry: number];\n\nexport const NODE_LEVEL = 0;\nexport const NODE_ENTRIES = 1;\n\n/**\n * The type of B+Tree node chunk data\n */\ntype BaseNode<V> = FrozenTag<\n readonly [level: number, entries: ReadonlyArray<Entry<V>>]\n>;\nexport type InternalNode = BaseNode<Hash>;\n\nexport type DataNode = BaseNode<FrozenJSONValue>;\n\nexport function makeNodeChunkData<V>(\n level: number,\n entries: ReadonlyArray<Entry<V>>,\n formatVersion: FormatVersion,\n): BaseNode<V> {\n return deepFreeze([\n level,\n (formatVersion >= FormatVersion.V7\n ? entries\n : entries.map(e => e.slice(0, 2))) as readonly ReadonlyJSONValue[],\n ]) as BaseNode<V>;\n}\n\nexport type Node = DataNode | InternalNode;\n\n/**\n * Describes the changes that happened to Replicache after a\n * {@link WriteTransaction} was committed.\n *\n * @experimental This type is experimental and may change in the future.\n */\nexport type Diff = IndexDiff | NoIndexDiff;\n\n/**\n * @experimental This type is experimental and may change in the future.\n */\nexport type IndexDiff = readonly DiffOperation<IndexKey>[];\n\n/**\n * @experimental This type is experimental and may change in the future.\n */\nexport type NoIndexDiff = readonly DiffOperation<string>[];\n\n/**\n * InternalDiff uses string keys even for the secondary index maps.\n */\nexport type InternalDiff = readonly InternalDiffOperation[];\n\nexport type DiffOperationAdd<Key, Value = ReadonlyJSONValue> = {\n readonly op: 'add';\n readonly key: Key;\n readonly newValue: Value;\n};\n\nexport type DiffOperationDel<Key, Value = ReadonlyJSONValue> = {\n readonly op: 'del';\n readonly key: Key;\n readonly oldValue: Value;\n};\n\nexport type DiffOperationChange<Key, Value = ReadonlyJSONValue> = {\n readonly op: 'change';\n readonly key: Key;\n readonly oldValue: Value;\n readonly newValue: Value;\n};\n\n/**\n * The individual parts describing the changes that happened to the Replicache\n * data. There are three different kinds of operations:\n * - `add`: A new entry was added.\n * - `del`: An entry was deleted.\n * - `change`: An entry was changed.\n *\n * @experimental This type is experimental and may change in the future.\n */\nexport type DiffOperation<Key> =\n | DiffOperationAdd<Key>\n | DiffOperationDel<Key>\n | DiffOperationChange<Key>;\n\n// Duplicated with DiffOperation to make the docs less confusing.\nexport type InternalDiffOperation<Key = string, Value = FrozenJSONValue> =\n | DiffOperationAdd<Key, Value>\n | DiffOperationDel<Key, Value>\n | DiffOperationChange<Key, Value>;\n\n/**\n * Finds the leaf where a key is (if present) or where it should go if not\n * present.\n */\nexport async function findLeaf(\n key: string,\n hash: Hash,\n source: BTreeRead,\n expectedRootHash: Hash,\n): Promise<DataNodeImpl> {\n const node = await source.getNode(hash);\n // The root changed. Try again\n if (expectedRootHash !== source.rootHash) {\n return findLeaf(key, source.rootHash, source, source.rootHash);\n }\n if (isDataNodeImpl(node)) {\n return node;\n }\n const {entries} = node;\n let i = binarySearch(key, entries);\n if (i === entries.length) {\n i--;\n }\n const entry = entries[i];\n return findLeaf(key, entry[1], source, expectedRootHash);\n}\n\ntype BinarySearchEntries = readonly Entry<unknown>[];\n\n/**\n * Does a binary search over entries\n *\n * If the key found then the return value is the index it was found at.\n *\n * If the key was *not* found then the return value is the index where it should\n * be inserted at\n */\nexport function binarySearch(\n key: string,\n entries: BinarySearchEntries,\n): number {\n return binarySearchWithFunc(entries.length, i =>\n compareUTF8(key, entries[i][0]),\n );\n}\n\nexport function binarySearchFound(\n i: number,\n entries: BinarySearchEntries,\n key: string,\n): boolean {\n return i !== entries.length && entries[i][0] === key;\n}\n\nexport function parseBTreeNode(\n v: unknown,\n formatVersion: FormatVersion,\n getSizeOfEntry: <K, V>(key: K, value: V) => number,\n): InternalNode | DataNode {\n if (skipBTreeNodeAsserts && formatVersion >= FormatVersion.V7) {\n return v as InternalNode | DataNode;\n }\n\n assertArray(v);\n assertDeepFrozen(v);\n // Be relaxed about what we accept.\n assert(v.length >= 2, 'Expected node array to have at least 2 elements');\n const [level, entries] = v;\n assertNumber(level);\n assertArray(entries);\n\n const f = level > 0 ? assertString : assertJSONValue;\n\n // For V7 we do not need to change the entries. Just assert that they are correct.\n if (formatVersion >= FormatVersion.V7) {\n for (const e of entries) {\n assertEntry(e, f);\n }\n return v as unknown as InternalNode | DataNode;\n }\n\n const newEntries = entries.map(e => convertNonV7Entry(e, f, getSizeOfEntry));\n return [level, newEntries] as unknown as InternalNode | DataNode;\n}\n\nfunction assertEntry(\n entry: unknown,\n f:\n | ((v: unknown) => asserts v is Hash)\n | ((v: unknown) => asserts v is JSONValue),\n): asserts entry is Entry<Hash | JSONValue> {\n assertArray(entry);\n // Be relaxed about what we accept.\n assert(entry.length >= 3, 'Expected entry array to have at least 3 elements');\n assertString(entry[0]);\n f(entry[1]);\n assertNumber(entry[2]);\n}\n\n/**\n * Converts an entry that was from a format version before V7 to the format\n * wanted by V7.\n */\nfunction convertNonV7Entry(\n entry: unknown,\n f:\n | ((v: unknown) => asserts v is Hash)\n | ((v: unknown) => asserts v is JSONValue),\n getSizeOfEntry: <K, V>(key: K, value: V) => number,\n): Entry<Hash | JSONValue> {\n assertArray(entry);\n assert(entry.length >= 2, 'Expected entry array to have at least 2 elements');\n assertString(entry[0]);\n f(entry[1]);\n const entrySize = getSizeOfEntry(entry[0], entry[1]);\n return [entry[0], entry[1], entrySize] as Entry<Hash | JSONValue>;\n}\n\nexport function isInternalNode(node: Node): node is InternalNode {\n return node[NODE_LEVEL] > 0;\n}\n\nabstract class NodeImpl<Value> {\n entries: Array<Entry<Value>>;\n hash: Hash;\n abstract readonly level: number;\n readonly isMutable: boolean;\n\n #childNodeSize = -1;\n\n constructor(entries: Array<Entry<Value>>, hash: Hash, isMutable: boolean) {\n this.entries = entries;\n this.hash = hash;\n this.isMutable = isMutable;\n }\n\n abstract set(\n key: string,\n value: FrozenJSONValue,\n entrySize: number,\n tree: BTreeWrite,\n ): Promise<NodeImpl<Value>>;\n\n abstract del(\n key: string,\n tree: BTreeWrite,\n ): Promise<NodeImpl<Value> | DataNodeImpl>;\n\n maxKey(): string {\n // oxlint-disable-next-line typescript/no-non-null-assertion\n return this.entries.at(-1)![0];\n }\n\n getChildNodeSize(tree: BTreeRead): number {\n if (this.#childNodeSize !== -1) {\n return this.#childNodeSize;\n }\n\n let sum = tree.chunkHeaderSize;\n for (const entry of this.entries) {\n sum += entry[2];\n }\n return (this.#childNodeSize = sum);\n }\n\n protected _updateNode(tree: BTreeWrite) {\n this.#childNodeSize = -1;\n tree.updateNode(\n this as NodeImpl<unknown> as DataNodeImpl | InternalNodeImpl,\n );\n }\n}\n\nexport function toChunkData<V>(\n node: NodeImpl<V>,\n formatVersion: FormatVersion,\n): BaseNode<V> {\n return makeNodeChunkData(node.level, node.entries, formatVersion);\n}\n\nexport class DataNodeImpl extends NodeImpl<FrozenJSONValue> {\n readonly level = 0;\n\n set(\n key: string,\n value: FrozenJSONValue,\n entrySize: number,\n tree: BTreeWrite,\n ): Promise<DataNodeImpl> {\n let deleteCount: number;\n const i = binarySearch(key, this.entries);\n if (!binarySearchFound(i, this.entries, key)) {\n // Not found, insert.\n deleteCount = 0;\n } else {\n deleteCount = 1;\n }\n\n return Promise.resolve(\n this.#splice(tree, i, deleteCount, [key, value, entrySize]),\n );\n }\n\n #splice(\n tree: BTreeWrite,\n start: number,\n deleteCount: number,\n ...items: Entry<FrozenJSONValue>[]\n ): DataNodeImpl {\n if (this.isMutable) {\n this.entries.splice(start, deleteCount, ...items);\n this._updateNode(tree);\n return this;\n }\n\n const entries = readonlySplice(this.entries, start, deleteCount, ...items);\n return tree.newDataNodeImpl(entries);\n }\n\n del(key: string, tree: BTreeWrite): Promise<DataNodeImpl> {\n const i = binarySearch(key, this.entries);\n if (!binarySearchFound(i, this.entries, key)) {\n // Not found. Return this without changes.\n return Promise.resolve(this);\n }\n\n // Found. Create new node or mutate existing one.\n return Promise.resolve(this.#splice(tree, i, 1));\n }\n\n async *keys(_tree: BTreeRead): AsyncGenerator<string, void> {\n for (const entry of this.entries) {\n yield entry[0];\n }\n }\n\n async *entriesIter(\n _tree: BTreeRead,\n ): AsyncGenerator<Entry<FrozenJSONValue>, void> {\n for (const entry of this.entries) {\n yield entry;\n }\n }\n}\n\nfunction readonlySplice<T>(\n array: ReadonlyArray<T>,\n start: number,\n deleteCount: number,\n ...items: T[]\n): T[] {\n const arr = array.slice(0, start);\n for (let i = 0; i < items.length; i++) {\n arr.push(items[i]);\n }\n for (let i = start + deleteCount; i < array.length; i++) {\n arr.push(array[i]);\n }\n return arr;\n}\n\nexport class InternalNodeImpl extends NodeImpl<Hash> {\n readonly level: number;\n\n constructor(\n entries: Array<Entry<Hash>>,\n hash: Hash,\n level: number,\n isMutable: boolean,\n ) {\n super(entries, hash, isMutable);\n this.level = level;\n }\n\n async set(\n key: string,\n value: FrozenJSONValue,\n entrySize: number,\n tree: BTreeWrite,\n ): Promise<InternalNodeImpl> {\n let i = binarySearch(key, this.entries);\n if (i === this.entries.length) {\n // We are going to insert into last (right most) leaf.\n i--;\n }\n\n const childHash = this.entries[i][1];\n const oldChildNode = await tree.getNode(childHash);\n\n const childNode = await oldChildNode.set(key, value, entrySize, tree);\n\n const childNodeSize = childNode.getChildNodeSize(tree);\n if (childNodeSize > tree.maxSize || childNodeSize < tree.minSize) {\n return this.#mergeAndPartition(tree, i, childNode);\n }\n\n const newEntry = createNewInternalEntryForNode(\n childNode,\n tree.getEntrySize,\n );\n return this.#replaceChild(tree, i, newEntry);\n }\n\n /**\n * This merges the child node entries with previous or next sibling and then\n * partitions the merged entries.\n */\n async #mergeAndPartition(\n tree: BTreeWrite,\n i: number,\n childNode: DataNodeImpl | InternalNodeImpl,\n ): Promise<InternalNodeImpl> {\n const level = this.level - 1;\n const thisEntries = this.entries;\n\n type IterableHashEntries = Iterable<Entry<Hash>>;\n\n let values: IterableHashEntries;\n let startIndex: number;\n let removeCount: number;\n if (i > 0) {\n const hash = thisEntries[i - 1][1];\n const previousSibling = await tree.getNode(hash);\n values = joinIterables(\n previousSibling.entries as IterableHashEntries,\n childNode.entries as IterableHashEntries,\n );\n startIndex = i - 1;\n removeCount = 2;\n } else if (i < thisEntries.length - 1) {\n const hash = thisEntries[i + 1][1];\n const nextSibling = await tree.getNode(hash);\n values = joinIterables(\n childNode.entries as IterableHashEntries,\n nextSibling.entries as IterableHashEntries,\n );\n startIndex = i;\n removeCount = 2;\n } else {\n values = childNode.entries as IterableHashEntries;\n startIndex = i;\n removeCount = 1;\n }\n\n const partitions = partition(\n values,\n value => value[2],\n tree.minSize - tree.chunkHeaderSize,\n tree.maxSize - tree.chunkHeaderSize,\n );\n\n // TODO: There are cases where we can reuse the old nodes. Creating new ones\n // means more memory churn but also more writes to the underlying KV store.\n const newEntries: Entry<Hash>[] = [];\n for (const entries of partitions) {\n const node = tree.newNodeImpl(entries, level);\n const newHashEntry = createNewInternalEntryForNode(\n node,\n tree.getEntrySize,\n );\n newEntries.push(newHashEntry);\n }\n\n if (this.isMutable) {\n this.entries.splice(startIndex, removeCount, ...newEntries);\n this._updateNode(tree);\n return this;\n }\n\n const entries = readonlySplice(\n thisEntries,\n startIndex,\n removeCount,\n ...newEntries,\n );\n\n return tree.newInternalNodeImpl(entries, this.level);\n }\n\n #replaceChild(\n tree: BTreeWrite,\n index: number,\n newEntry: Entry<Hash>,\n ): InternalNodeImpl {\n if (this.isMutable) {\n this.entries.splice(index, 1, newEntry);\n this._updateNode(tree);\n return this;\n }\n const entries = readonlySplice(this.entries, index, 1, newEntry);\n return tree.newInternalNodeImpl(entries, this.level);\n }\n\n async del(\n key: string,\n tree: BTreeWrite,\n ): Promise<InternalNodeImpl | DataNodeImpl> {\n const i = binarySearch(key, this.entries);\n if (i === this.entries.length) {\n // Key is larger than maxKey of rightmost entry so it is not present.\n return this;\n }\n\n const childHash = this.entries[i][1];\n const oldChildNode = await tree.getNode(childHash);\n const oldHash = oldChildNode.hash;\n\n const childNode = await oldChildNode.del(key, tree);\n if (childNode.hash === oldHash) {\n // Not changed so not found.\n return this;\n }\n\n if (childNode.entries.length === 0) {\n // Subtree is now empty. Remove internal node.\n const entries = readonlySplice(this.entries, i, 1);\n return tree.newInternalNodeImpl(entries, this.level);\n }\n\n if (i === 0 && this.entries.length === 1) {\n // There was only one node at this level and it was removed. We can return\n // the modified subtree.\n return childNode;\n }\n\n // The child node is still a good size.\n if (childNode.getChildNodeSize(tree) > tree.minSize) {\n // No merging needed.\n const entry = createNewInternalEntryForNode(childNode, tree.getEntrySize);\n return this.#replaceChild(tree, i, entry);\n }\n\n // Child node size is too small.\n return this.#mergeAndPartition(tree, i, childNode);\n }\n\n async *keys(tree: BTreeRead): AsyncGenerator<string, void> {\n for (const entry of this.entries) {\n const childNode = await tree.getNode(entry[1]);\n yield* childNode.keys(tree);\n }\n }\n\n async *entriesIter(\n tree: BTreeRead,\n ): AsyncGenerator<Entry<FrozenJSONValue>, void> {\n for (const entry of this.entries) {\n const childNode = await tree.getNode(entry[1]);\n yield* childNode.entriesIter(tree);\n }\n }\n\n getChildren(\n start: number,\n length: number,\n tree: BTreeRead,\n ): Promise<Array<InternalNodeImpl | DataNodeImpl>> {\n const ps: Promise<DataNodeImpl | InternalNodeImpl>[] = [];\n for (let i = start; i < length && i < this.entries.length; i++) {\n ps.push(tree.getNode(this.entries[i][1]));\n }\n return Promise.all(ps);\n }\n\n async getCompositeChildren(\n start: number,\n length: number,\n tree: BTreeRead,\n ): Promise<InternalNodeImpl | DataNodeImpl> {\n const {level} = this;\n\n if (length === 0) {\n return new InternalNodeImpl([], newRandomHash(), level - 1, true);\n }\n\n const output = await this.getChildren(start, start + length, tree);\n\n if (level > 1) {\n const entries: Entry<Hash>[] = [];\n for (const child of output as InternalNodeImpl[]) {\n entries.push(...child.entries);\n }\n return new InternalNodeImpl(entries, newRandomHash(), level - 1, true);\n }\n\n assert(level === 1, 'Expected level to be 1');\n const entries: Entry<FrozenJSONValue>[] = [];\n for (const child of output as DataNodeImpl[]) {\n entries.push(...child.entries);\n }\n return new DataNodeImpl(entries, newRandomHash(), true);\n }\n}\n\nexport function newNodeImpl(\n entries: Array<Entry<FrozenJSONValue>>,\n hash: Hash,\n level: number,\n isMutable: boolean,\n): DataNodeImpl;\nexport function newNodeImpl(\n entries: Array<Entry<Hash>>,\n hash: Hash,\n level: number,\n isMutable: boolean,\n): InternalNodeImpl;\nexport function newNodeImpl(\n entries: Array<Entry<FrozenJSONValue>> | Array<Entry<Hash>>,\n hash: Hash,\n level: number,\n isMutable: boolean,\n): DataNodeImpl | InternalNodeImpl;\nexport function newNodeImpl(\n entries: Array<Entry<FrozenJSONValue>> | Array<Entry<Hash>>,\n hash: Hash,\n level: number,\n isMutable: boolean,\n): DataNodeImpl | InternalNodeImpl {\n if (level === 0) {\n return new DataNodeImpl(\n entries as Entry<FrozenJSONValue>[],\n hash,\n isMutable,\n );\n }\n return new InternalNodeImpl(entries as Entry<Hash>[], hash, level, isMutable);\n}\n\nexport function isDataNodeImpl(\n node: DataNodeImpl | InternalNodeImpl,\n): node is DataNodeImpl {\n return node.level === 0;\n}\n\nexport function partition<T>(\n values: Iterable<T>,\n // This is the size of each Entry\n getSizeOfEntry: (v: T) => number,\n min: number,\n max: number,\n): T[][] {\n const partitions: T[][] = [];\n const sizes: number[] = [];\n let sum = 0;\n let accum: T[] = [];\n for (const value of values) {\n const size = getSizeOfEntry(value);\n if (size >= max) {\n if (accum.length > 0) {\n partitions.push(accum);\n sizes.push(sum);\n }\n partitions.push([value]);\n sizes.push(size);\n sum = 0;\n accum = [];\n } else if (sum + size >= min) {\n accum.push(value);\n partitions.push(accum);\n sizes.push(sum + size);\n sum = 0;\n accum = [];\n } else {\n sum += size;\n accum.push(value);\n }\n }\n\n if (sum > 0) {\n // oxlint-disable-next-line typescript/no-non-null-assertion\n if (sizes.length > 0 && sum + sizes.at(-1)! <= max) {\n // oxlint-disable-next-line typescript/no-non-null-assertion\n partitions.at(-1)!.push(...accum);\n } else {\n partitions.push(accum);\n }\n }\n\n return partitions;\n}\n\nexport const emptyDataNode = makeNodeChunkData<ReadonlyJSONValue>(\n 0,\n [],\n FormatVersion.Latest,\n);\nexport const emptyDataNodeImpl = new DataNodeImpl([], emptyHash, false);\n\nexport function createNewInternalEntryForNode(\n node: NodeImpl<unknown>,\n getSizeOfEntry: <K, V>(k: K, v: V) => number,\n): [string, Hash, number] {\n const key = node.maxKey();\n const value = node.hash;\n const size = getSizeOfEntry(key, value);\n return [key, value, size];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"node.js","names":["#childNodeSize","#splice","#mergeAndPartition","#replaceChild"],"sources":["../../../../../replicache/src/btree/node.ts"],"sourcesContent":["import {compareUTF8} from 'compare-utf8';\nimport {\n assert,\n assertArray,\n assertNumber,\n assertString,\n} from '../../../shared/src/asserts.ts';\nimport {binarySearch as binarySearchWithFunc} from '../../../shared/src/binary-search.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport {joinIterables} from '../../../shared/src/iterables.ts';\nimport {\n type JSONValue,\n type ReadonlyJSONValue,\n assertJSONValue,\n} from '../../../shared/src/json.ts';\nimport {skipBTreeNodeAsserts} from '../config.ts';\nimport type {IndexKey} from '../db/index.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport {\n type FrozenJSONValue,\n type FrozenTag,\n assertDeepFrozen,\n deepFreeze,\n} from '../frozen-json.ts';\nimport {type Hash, emptyHash, newRandomHash} from '../hash.ts';\nimport type {BTreeRead} from './read.ts';\nimport type {BTreeWrite} from './write.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport type Entry<V> = readonly [key: string, value: V, sizeOfEntry: number];\n\nexport const NODE_LEVEL = 0;\nexport const NODE_ENTRIES = 1;\n\n/**\n * The type of B+Tree node chunk data\n */\ntype BaseNode<V> = FrozenTag<\n readonly [level: number, entries: ReadonlyArray<Entry<V>>]\n>;\nexport type InternalNode = BaseNode<Hash>;\n\nexport type DataNode = BaseNode<FrozenJSONValue>;\n\nexport function makeNodeChunkData<V>(\n level: number,\n entries: ReadonlyArray<Entry<V>>,\n formatVersion: FormatVersion,\n): BaseNode<V> {\n return deepFreeze([\n level,\n (formatVersion >= FormatVersion.V7\n ? entries\n : entries.map(e => e.slice(0, 2))) as readonly ReadonlyJSONValue[],\n ]) as BaseNode<V>;\n}\n\nexport type Node = DataNode | InternalNode;\n\n/**\n * Describes the changes that happened to Replicache after a\n * {@link WriteTransaction} was committed.\n *\n * @experimental This type is experimental and may change in the future.\n */\nexport type Diff = IndexDiff | NoIndexDiff;\n\n/**\n * @experimental This type is experimental and may change in the future.\n */\nexport type IndexDiff = readonly DiffOperation<IndexKey>[];\n\n/**\n * @experimental This type is experimental and may change in the future.\n */\nexport type NoIndexDiff = readonly DiffOperation<string>[];\n\n/**\n * InternalDiff uses string keys even for the secondary index maps.\n */\nexport type InternalDiff = readonly InternalDiffOperation[];\n\nexport type DiffOperationAdd<Key, Value = ReadonlyJSONValue> = {\n readonly op: 'add';\n readonly key: Key;\n readonly newValue: Value;\n};\n\nexport type DiffOperationDel<Key, Value = ReadonlyJSONValue> = {\n readonly op: 'del';\n readonly key: Key;\n readonly oldValue: Value;\n};\n\nexport type DiffOperationChange<Key, Value = ReadonlyJSONValue> = {\n readonly op: 'change';\n readonly key: Key;\n readonly oldValue: Value;\n readonly newValue: Value;\n};\n\n/**\n * The individual parts describing the changes that happened to the Replicache\n * data. There are three different kinds of operations:\n * - `add`: A new entry was added.\n * - `del`: An entry was deleted.\n * - `change`: An entry was changed.\n *\n * @experimental This type is experimental and may change in the future.\n */\nexport type DiffOperation<Key> =\n | DiffOperationAdd<Key>\n | DiffOperationDel<Key>\n | DiffOperationChange<Key>;\n\n// Duplicated with DiffOperation to make the docs less confusing.\nexport type InternalDiffOperation<Key = string, Value = FrozenJSONValue> =\n | DiffOperationAdd<Key, Value>\n | DiffOperationDel<Key, Value>\n | DiffOperationChange<Key, Value>;\n\n/**\n * Finds the leaf where a key is (if present) or where it should go if not\n * present.\n */\nexport async function findLeaf(\n key: string,\n hash: Hash,\n source: BTreeRead,\n expectedRootHash: Hash,\n): Promise<DataNodeImpl> {\n const node = await source.getNode(hash);\n // The root changed. Try again\n if (expectedRootHash !== source.rootHash) {\n return findLeaf(key, source.rootHash, source, source.rootHash);\n }\n if (isDataNodeImpl(node)) {\n return node;\n }\n const {entries} = node;\n let i = binarySearch(key, entries);\n if (i === entries.length) {\n i--;\n }\n const entry = entries[i];\n return findLeaf(key, entry[1], source, expectedRootHash);\n}\n\ntype BinarySearchEntries = readonly Entry<unknown>[];\n\n/**\n * Does a binary search over entries\n *\n * If the key found then the return value is the index it was found at.\n *\n * If the key was *not* found then the return value is the index where it should\n * be inserted at\n */\nexport function binarySearch(\n key: string,\n entries: BinarySearchEntries,\n): number {\n return binarySearchWithFunc(entries.length, i =>\n compareUTF8(key, entries[i][0]),\n );\n}\n\nexport function binarySearchFound(\n i: number,\n entries: BinarySearchEntries,\n key: string,\n): boolean {\n return i !== entries.length && entries[i][0] === key;\n}\n\nexport function parseBTreeNode(\n v: unknown,\n formatVersion: FormatVersion,\n getSizeOfEntry: <K, V>(key: K, value: V) => number,\n): InternalNode | DataNode {\n if (skipBTreeNodeAsserts && formatVersion >= FormatVersion.V7) {\n return v as InternalNode | DataNode;\n }\n\n assertArray(v);\n assertDeepFrozen(v);\n // Be relaxed about what we accept.\n assert(v.length >= 2, 'Expected node array to have at least 2 elements');\n const [level, entries] = v;\n assertNumber(level);\n assertArray(entries);\n\n const f = level > 0 ? assertString : assertJSONValue;\n\n // For V7 we do not need to change the entries. Just assert that they are correct.\n if (formatVersion >= FormatVersion.V7) {\n for (const e of entries) {\n assertEntry(e, f);\n }\n return v as unknown as InternalNode | DataNode;\n }\n\n const newEntries = entries.map(e => convertNonV7Entry(e, f, getSizeOfEntry));\n return [level, newEntries] as unknown as InternalNode | DataNode;\n}\n\nfunction assertEntry(\n entry: unknown,\n f:\n | ((v: unknown) => asserts v is Hash)\n | ((v: unknown) => asserts v is JSONValue),\n): asserts entry is Entry<Hash | JSONValue> {\n assertArray(entry);\n // Be relaxed about what we accept.\n assert(entry.length >= 3, 'Expected entry array to have at least 3 elements');\n assertString(entry[0]);\n f(entry[1]);\n assertNumber(entry[2]);\n}\n\n/**\n * Converts an entry that was from a format version before V7 to the format\n * wanted by V7.\n */\nfunction convertNonV7Entry(\n entry: unknown,\n f:\n | ((v: unknown) => asserts v is Hash)\n | ((v: unknown) => asserts v is JSONValue),\n getSizeOfEntry: <K, V>(key: K, value: V) => number,\n): Entry<Hash | JSONValue> {\n assertArray(entry);\n assert(entry.length >= 2, 'Expected entry array to have at least 2 elements');\n assertString(entry[0]);\n f(entry[1]);\n const entrySize = getSizeOfEntry(entry[0], entry[1]);\n return [entry[0], entry[1], entrySize] as Entry<Hash | JSONValue>;\n}\n\nexport function isInternalNode(node: Node): node is InternalNode {\n return node[NODE_LEVEL] > 0;\n}\n\nabstract class NodeImpl<Value> {\n entries: Array<Entry<Value>>;\n hash: Hash;\n abstract readonly level: number;\n readonly isMutable: boolean;\n\n #childNodeSize = -1;\n\n constructor(entries: Array<Entry<Value>>, hash: Hash, isMutable: boolean) {\n this.entries = entries;\n this.hash = hash;\n this.isMutable = isMutable;\n }\n\n abstract set(\n key: string,\n value: FrozenJSONValue,\n entrySize: number,\n tree: BTreeWrite,\n ): Promise<NodeImpl<Value>>;\n\n abstract del(\n key: string,\n tree: BTreeWrite,\n ): Promise<NodeImpl<Value> | DataNodeImpl>;\n\n maxKey(): string {\n // oxlint-disable-next-line typescript/no-non-null-assertion\n return this.entries.at(-1)![0];\n }\n\n getChildNodeSize(tree: BTreeRead): number {\n if (this.#childNodeSize !== -1) {\n return this.#childNodeSize;\n }\n\n let sum = tree.chunkHeaderSize;\n for (const entry of this.entries) {\n sum += entry[2];\n }\n return (this.#childNodeSize = sum);\n }\n\n protected _updateNode(tree: BTreeWrite) {\n this.#childNodeSize = -1;\n tree.updateNode(\n this as NodeImpl<unknown> as DataNodeImpl | InternalNodeImpl,\n );\n }\n}\n\nexport function toChunkData<V>(\n node: NodeImpl<V>,\n formatVersion: FormatVersion,\n): BaseNode<V> {\n return makeNodeChunkData(node.level, node.entries, formatVersion);\n}\n\nexport class DataNodeImpl extends NodeImpl<FrozenJSONValue> {\n readonly level = 0;\n\n set(\n key: string,\n value: FrozenJSONValue,\n entrySize: number,\n tree: BTreeWrite,\n ): Promise<DataNodeImpl> {\n let deleteCount: number;\n const i = binarySearch(key, this.entries);\n if (!binarySearchFound(i, this.entries, key)) {\n // Not found, insert.\n deleteCount = 0;\n } else {\n deleteCount = 1;\n }\n\n return Promise.resolve(\n this.#splice(tree, i, deleteCount, [key, value, entrySize]),\n );\n }\n\n #splice(\n tree: BTreeWrite,\n start: number,\n deleteCount: number,\n ...items: Entry<FrozenJSONValue>[]\n ): DataNodeImpl {\n if (this.isMutable) {\n this.entries.splice(start, deleteCount, ...items);\n this._updateNode(tree);\n return this;\n }\n\n const entries = readonlySplice(this.entries, start, deleteCount, ...items);\n return tree.newDataNodeImpl(entries);\n }\n\n del(key: string, tree: BTreeWrite): Promise<DataNodeImpl> {\n const i = binarySearch(key, this.entries);\n if (!binarySearchFound(i, this.entries, key)) {\n // Not found. Return this without changes.\n return Promise.resolve(this);\n }\n\n // Found. Create new node or mutate existing one.\n return Promise.resolve(this.#splice(tree, i, 1));\n }\n\n async *keys(_tree: BTreeRead): AsyncGenerator<string, void> {\n for (const entry of this.entries) {\n yield entry[0];\n }\n }\n\n async *entriesIter(\n _tree: BTreeRead,\n ): AsyncGenerator<Entry<FrozenJSONValue>, void> {\n for (const entry of this.entries) {\n yield entry;\n }\n }\n}\n\nfunction readonlySplice<T>(\n array: ReadonlyArray<T>,\n start: number,\n deleteCount: number,\n ...items: T[]\n): T[] {\n const arr = array.slice(0, start);\n for (let i = 0; i < items.length; i++) {\n arr.push(items[i]);\n }\n for (let i = start + deleteCount; i < array.length; i++) {\n arr.push(array[i]);\n }\n return arr;\n}\n\nexport class InternalNodeImpl extends NodeImpl<Hash> {\n readonly level: number;\n\n constructor(\n entries: Array<Entry<Hash>>,\n hash: Hash,\n level: number,\n isMutable: boolean,\n ) {\n super(entries, hash, isMutable);\n this.level = level;\n }\n\n async set(\n key: string,\n value: FrozenJSONValue,\n entrySize: number,\n tree: BTreeWrite,\n ): Promise<InternalNodeImpl> {\n let i = binarySearch(key, this.entries);\n if (i === this.entries.length) {\n // We are going to insert into last (right most) leaf.\n i--;\n }\n\n const childHash = this.entries[i][1];\n const oldChildNode = await tree.getNode(childHash);\n\n const childNode = await oldChildNode.set(key, value, entrySize, tree);\n\n const childNodeSize = childNode.getChildNodeSize(tree);\n if (childNodeSize > tree.maxSize || childNodeSize < tree.minSize) {\n return this.#mergeAndPartition(tree, i, childNode);\n }\n\n const newEntry = createNewInternalEntryForNode(\n childNode,\n tree.getEntrySize,\n );\n return this.#replaceChild(tree, i, newEntry);\n }\n\n /**\n * This merges the child node entries with previous or next sibling and then\n * partitions the merged entries.\n */\n async #mergeAndPartition(\n tree: BTreeWrite,\n i: number,\n childNode: DataNodeImpl | InternalNodeImpl,\n ): Promise<InternalNodeImpl> {\n const level = this.level - 1;\n const thisEntries = this.entries;\n\n type IterableHashEntries = Iterable<Entry<Hash>>;\n\n let values: IterableHashEntries;\n let startIndex: number;\n let removeCount: number;\n if (i > 0) {\n const hash = thisEntries[i - 1][1];\n const previousSibling = await tree.getNode(hash);\n values = joinIterables(\n previousSibling.entries as IterableHashEntries,\n childNode.entries as IterableHashEntries,\n );\n startIndex = i - 1;\n removeCount = 2;\n } else if (i < thisEntries.length - 1) {\n const hash = thisEntries[i + 1][1];\n const nextSibling = await tree.getNode(hash);\n values = joinIterables(\n childNode.entries as IterableHashEntries,\n nextSibling.entries as IterableHashEntries,\n );\n startIndex = i;\n removeCount = 2;\n } else {\n values = childNode.entries as IterableHashEntries;\n startIndex = i;\n removeCount = 1;\n }\n\n const newEntries = partition(\n values,\n value => value[2],\n tree.minSize - tree.chunkHeaderSize,\n tree.maxSize - tree.chunkHeaderSize,\n entries => {\n const node = tree.newNodeImpl(entries, level);\n return createNewInternalEntryForNode(node, tree.getEntrySize);\n },\n );\n\n if (this.isMutable) {\n this.entries.splice(startIndex, removeCount, ...newEntries);\n this._updateNode(tree);\n return this;\n }\n\n const entries = readonlySplice(\n thisEntries,\n startIndex,\n removeCount,\n ...newEntries,\n );\n\n return tree.newInternalNodeImpl(entries, this.level);\n }\n\n #replaceChild(\n tree: BTreeWrite,\n index: number,\n newEntry: Entry<Hash>,\n ): InternalNodeImpl {\n if (this.isMutable) {\n this.entries.splice(index, 1, newEntry);\n this._updateNode(tree);\n return this;\n }\n const entries = readonlySplice(this.entries, index, 1, newEntry);\n return tree.newInternalNodeImpl(entries, this.level);\n }\n\n async del(\n key: string,\n tree: BTreeWrite,\n ): Promise<InternalNodeImpl | DataNodeImpl> {\n const i = binarySearch(key, this.entries);\n if (i === this.entries.length) {\n // Key is larger than maxKey of rightmost entry so it is not present.\n return this;\n }\n\n const childHash = this.entries[i][1];\n const oldChildNode = await tree.getNode(childHash);\n const oldHash = oldChildNode.hash;\n\n const childNode = await oldChildNode.del(key, tree);\n if (childNode.hash === oldHash) {\n // Not changed so not found.\n return this;\n }\n\n if (childNode.entries.length === 0) {\n // Subtree is now empty. Remove internal node.\n const entries = readonlySplice(this.entries, i, 1);\n return tree.newInternalNodeImpl(entries, this.level);\n }\n\n if (i === 0 && this.entries.length === 1) {\n // There was only one node at this level and it was removed. We can return\n // the modified subtree.\n return childNode;\n }\n\n // The child node is still a good size.\n if (childNode.getChildNodeSize(tree) > tree.minSize) {\n // No merging needed.\n const entry = createNewInternalEntryForNode(childNode, tree.getEntrySize);\n return this.#replaceChild(tree, i, entry);\n }\n\n // Child node size is too small.\n return this.#mergeAndPartition(tree, i, childNode);\n }\n\n async *keys(tree: BTreeRead): AsyncGenerator<string, void> {\n for (const entry of this.entries) {\n const childNode = await tree.getNode(entry[1]);\n yield* childNode.keys(tree);\n }\n }\n\n async *entriesIter(\n tree: BTreeRead,\n ): AsyncGenerator<Entry<FrozenJSONValue>, void> {\n for (const entry of this.entries) {\n const childNode = await tree.getNode(entry[1]);\n yield* childNode.entriesIter(tree);\n }\n }\n\n getChildren(\n start: number,\n length: number,\n tree: BTreeRead,\n ): Promise<Array<InternalNodeImpl | DataNodeImpl>> {\n const ps: Promise<DataNodeImpl | InternalNodeImpl>[] = [];\n for (let i = start; i < length && i < this.entries.length; i++) {\n ps.push(tree.getNode(this.entries[i][1]));\n }\n return Promise.all(ps);\n }\n\n async getCompositeChildren(\n start: number,\n length: number,\n tree: BTreeRead,\n ): Promise<InternalNodeImpl | DataNodeImpl> {\n const {level} = this;\n\n if (length === 0) {\n return new InternalNodeImpl([], newRandomHash(), level - 1, true);\n }\n\n const output = await this.getChildren(start, start + length, tree);\n\n if (level > 1) {\n const entries: Entry<Hash>[] = [];\n for (const child of output as InternalNodeImpl[]) {\n entries.push(...child.entries);\n }\n return new InternalNodeImpl(entries, newRandomHash(), level - 1, true);\n }\n\n assert(level === 1, 'Expected level to be 1');\n const entries: Entry<FrozenJSONValue>[] = [];\n for (const child of output as DataNodeImpl[]) {\n entries.push(...child.entries);\n }\n return new DataNodeImpl(entries, newRandomHash(), true);\n }\n}\n\nexport function newNodeImpl(\n entries: Array<Entry<FrozenJSONValue>>,\n hash: Hash,\n level: number,\n isMutable: boolean,\n): DataNodeImpl;\nexport function newNodeImpl(\n entries: Array<Entry<Hash>>,\n hash: Hash,\n level: number,\n isMutable: boolean,\n): InternalNodeImpl;\nexport function newNodeImpl(\n entries: Array<Entry<FrozenJSONValue>> | Array<Entry<Hash>>,\n hash: Hash,\n level: number,\n isMutable: boolean,\n): DataNodeImpl | InternalNodeImpl;\nexport function newNodeImpl(\n entries: Array<Entry<FrozenJSONValue>> | Array<Entry<Hash>>,\n hash: Hash,\n level: number,\n isMutable: boolean,\n): DataNodeImpl | InternalNodeImpl {\n if (level === 0) {\n return new DataNodeImpl(\n entries as Entry<FrozenJSONValue>[],\n hash,\n isMutable,\n );\n }\n return new InternalNodeImpl(entries as Entry<Hash>[], hash, level, isMutable);\n}\n\nexport function isDataNodeImpl(\n node: DataNodeImpl | InternalNodeImpl,\n): node is DataNodeImpl {\n return node.level === 0;\n}\n\nexport function partition<T, R>(\n values: Iterable<T>,\n // This is the size of each Entry\n getSizeOfEntry: (v: T) => number,\n min: number,\n max: number,\n create: (entries: T[]) => R,\n): R[] {\n const results: R[] = [];\n let sum = 0;\n let accum: T[] = [];\n // The most recently finalized partition, held back until we know it won't be\n // merged with trailing entries.\n let lastPartition: T[] | null = null;\n let lastSize = 0;\n\n function commitLast() {\n if (lastPartition !== null) {\n results.push(create(lastPartition));\n lastPartition = null;\n }\n }\n\n for (const value of values) {\n const size = getSizeOfEntry(value);\n if (size >= max) {\n if (accum.length > 0) {\n commitLast();\n lastPartition = accum;\n lastSize = sum;\n accum = [];\n sum = 0;\n }\n commitLast();\n lastPartition = [value];\n lastSize = size;\n } else if (sum + size >= min) {\n accum.push(value);\n commitLast();\n lastPartition = accum;\n lastSize = sum + size;\n accum = [];\n sum = 0;\n } else {\n sum += size;\n accum.push(value);\n }\n }\n\n if (sum > 0) {\n if (lastPartition !== null && sum + lastSize <= max) {\n lastPartition.push(...accum);\n commitLast();\n } else {\n commitLast();\n results.push(create(accum));\n }\n } else {\n commitLast();\n }\n\n return results;\n}\n\nexport const emptyDataNode = makeNodeChunkData<ReadonlyJSONValue>(\n 0,\n [],\n FormatVersion.Latest,\n);\nexport const emptyDataNodeImpl = new DataNodeImpl([], emptyHash, false);\n\nexport function createNewInternalEntryForNode(\n node: NodeImpl<unknown>,\n getSizeOfEntry: <K, V>(k: K, v: V) => number,\n): [string, Hash, number] {\n const key = node.maxKey();\n const value = node.hash;\n const size = getSizeOfEntry(key, value);\n return [key, value, size];\n}\n"],"mappings":"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"}
|
|
@@ -65,9 +65,8 @@ var BTreeWrite = class extends BTreeRead {
|
|
|
65
65
|
const rootNode = await oldRootNode.set(key, value, entrySize, this);
|
|
66
66
|
if (rootNode.getChildNodeSize(this) > this.maxSize) {
|
|
67
67
|
const headerSize = this.chunkHeaderSize;
|
|
68
|
-
const partitions = partition(rootNode.entries, (value) => value[2], this.minSize - headerSize, this.maxSize - headerSize);
|
|
69
68
|
const { level } = rootNode;
|
|
70
|
-
const entries =
|
|
69
|
+
const entries = partition(rootNode.entries, (value) => value[2], this.minSize - headerSize, this.maxSize - headerSize, (entries) => {
|
|
71
70
|
return createNewInternalEntryForNode(this.newNodeImpl(entries, level), this.getEntrySize);
|
|
72
71
|
});
|
|
73
72
|
this.rootHash = this.newInternalNodeImpl(entries, level + 1).hash;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write.js","names":["#lock","#modified","#addToModified"],"sources":["../../../../../replicache/src/btree/write.ts"],"sourcesContent":["import {Lock} from '@rocicorp/lock';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {getSizeOfEntry} from '../../../shared/src/size-of-value.ts';\nimport {type Chunk, type CreateChunk, toRefs} from '../dag/chunk.ts';\nimport type {Write} from '../dag/store.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, emptyHash, newRandomHash} from '../hash.ts';\nimport {\n DataNodeImpl,\n type Entry,\n InternalNodeImpl,\n createNewInternalEntryForNode,\n emptyDataNode,\n isDataNodeImpl,\n newNodeImpl,\n partition,\n toChunkData,\n} from './node.ts';\nimport {BTreeRead} from './read.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport class BTreeWrite extends BTreeRead {\n /**\n * This rw lock is used to ensure we do not mutate the btree in parallel. It\n * would be a problem if we didn't have the lock in cases like this:\n *\n * ```ts\n * const p1 = tree.put('a', 0);\n * const p2 = tree.put('b', 1);\n * await p1;\n * await p2;\n * ```\n *\n * because both `p1` and `p2` would start from the old root hash but a put\n * changes the root hash so the two concurrent puts would lead to only one of\n * them actually working, and it is not deterministic which one would finish\n * last.\n */\n readonly #lock = new Lock();\n readonly #modified: Map<Hash, DataNodeImpl | InternalNodeImpl> = new Map();\n\n declare protected _dagRead: Write;\n\n readonly minSize: number;\n readonly maxSize: number;\n\n constructor(\n dagWrite: Write,\n formatVersion: FormatVersion,\n root: Hash = emptyHash,\n minSize = 8 * 1024,\n maxSize = 16 * 1024,\n getEntrySize: <K, V>(k: K, v: V) => number = getSizeOfEntry,\n chunkHeaderSize?: number,\n ) {\n super(dagWrite, formatVersion, root, getEntrySize, chunkHeaderSize);\n\n this.minSize = minSize;\n this.maxSize = maxSize;\n }\n\n #addToModified(node: DataNodeImpl | InternalNodeImpl): void {\n assert(node.isMutable, 'Expected node to be mutable');\n this.#modified.set(node.hash, node);\n this._cache.set(node.hash, node);\n }\n\n updateNode(node: DataNodeImpl | InternalNodeImpl): void {\n assert(node.isMutable, 'Expected node to be mutable');\n this.#modified.delete(node.hash);\n node.hash = newRandomHash();\n this.#addToModified(node);\n }\n\n newInternalNodeImpl(\n entries: Array<Entry<Hash>>,\n level: number,\n ): InternalNodeImpl {\n const n = new InternalNodeImpl(entries, newRandomHash(), level, true);\n this.#addToModified(n);\n return n;\n }\n\n newDataNodeImpl(entries: Entry<FrozenJSONValue>[]): DataNodeImpl {\n const n = new DataNodeImpl(entries, newRandomHash(), true);\n this.#addToModified(n);\n return n;\n }\n\n newNodeImpl(entries: Entry<FrozenJSONValue>[], level: number): DataNodeImpl;\n newNodeImpl(entries: Entry<Hash>[], level: number): InternalNodeImpl;\n newNodeImpl(\n entries: Entry<Hash>[] | Entry<FrozenJSONValue>[],\n level: number,\n ): InternalNodeImpl | DataNodeImpl;\n newNodeImpl(\n entries: Entry<Hash>[] | Entry<FrozenJSONValue>[],\n level: number,\n ): InternalNodeImpl | DataNodeImpl {\n const n = newNodeImpl(entries, newRandomHash(), level, true);\n this.#addToModified(n);\n return n;\n }\n\n put(key: string, value: FrozenJSONValue): Promise<void> {\n return this.#lock.withLock(async () => {\n const oldRootNode = await this.getNode(this.rootHash);\n const entrySize = this.getEntrySize(key, value);\n const rootNode = await oldRootNode.set(key, value, entrySize, this);\n\n // We do the rebalancing in the parent so we need to do it here as well.\n if (rootNode.getChildNodeSize(this) > this.maxSize) {\n const headerSize = this.chunkHeaderSize;\n const partitions = partition(\n rootNode.entries,\n value => value[2],\n this.minSize - headerSize,\n this.maxSize - headerSize,\n );\n const {level} = rootNode;\n const entries: Entry<Hash>[] = partitions.map(entries => {\n const node = this.newNodeImpl(entries, level);\n return createNewInternalEntryForNode(node, this.getEntrySize);\n });\n const newRoot = this.newInternalNodeImpl(entries, level + 1);\n this.rootHash = newRoot.hash;\n return;\n }\n\n this.rootHash = rootNode.hash;\n });\n }\n\n del(key: string): Promise<boolean> {\n return this.#lock.withLock(async () => {\n const oldRootNode = await this.getNode(this.rootHash);\n const newRootNode = await oldRootNode.del(key, this);\n\n // No need to rebalance here since if root gets too small there is nothing\n // we can do about that.\n const found = this.rootHash !== newRootNode.hash;\n if (found) {\n // Flatten one layer.\n if (newRootNode.level > 0 && newRootNode.entries.length === 1) {\n this.rootHash = (newRootNode as InternalNodeImpl).entries[0][1];\n } else {\n this.rootHash = newRootNode.hash;\n }\n }\n\n return found;\n });\n }\n\n clear(): Promise<void> {\n return this.#lock.withLock(() => {\n this.#modified.clear();\n this.rootHash = emptyHash;\n });\n }\n\n flush(): Promise<Hash> {\n return this.#lock.withLock(async () => {\n const dagWrite = this._dagRead;\n\n if (this.rootHash === emptyHash) {\n // Write a chunk for the empty tree.\n const chunk = dagWrite.createChunk(emptyDataNode, []);\n await dagWrite.putChunk(chunk as Chunk<ReadonlyJSONValue>);\n return chunk.hash;\n }\n\n const newChunks: Chunk[] = [];\n const newRoot = gatherNewChunks(\n this.rootHash,\n newChunks,\n dagWrite.createChunk,\n this.#modified,\n this._formatVersion,\n );\n await Promise.all(newChunks.map(chunk => dagWrite.putChunk(chunk)));\n this.#modified.clear();\n this.rootHash = newRoot;\n return newRoot;\n });\n }\n}\n\nfunction gatherNewChunks(\n hash: Hash,\n newChunks: Chunk[],\n createChunk: CreateChunk,\n modified: Map<Hash, DataNodeImpl | InternalNodeImpl>,\n formatVersion: FormatVersion,\n): Hash {\n const node = modified.get(hash);\n if (node === undefined) {\n // Not modified, use the original.\n return hash;\n }\n\n if (isDataNodeImpl(node)) {\n const chunk = createChunk(toChunkData(node, formatVersion), []);\n newChunks.push(chunk);\n return chunk.hash;\n }\n\n // The BTree cannot have duplicate keys so the child entry hashes are unique.\n // No need fot a set to dedupe here.\n const refs: Hash[] = [];\n const {entries} = node;\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n const childHash = entry[1];\n const newChildHash = gatherNewChunks(\n childHash,\n newChunks,\n createChunk,\n modified,\n formatVersion,\n );\n if (newChildHash !== childHash) {\n // MUTATES the entries!\n // Hashes do not change the size of the entry because all hashes have the same length\n entries[i] = [entry[0], newChildHash, entry[2]];\n }\n refs.push(newChildHash);\n }\n const chunk = createChunk(toChunkData(node, formatVersion), toRefs(refs));\n newChunks.push(chunk);\n return chunk.hash;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"write.js","names":["#lock","#modified","#addToModified"],"sources":["../../../../../replicache/src/btree/write.ts"],"sourcesContent":["import {Lock} from '@rocicorp/lock';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {getSizeOfEntry} from '../../../shared/src/size-of-value.ts';\nimport {type Chunk, type CreateChunk, toRefs} from '../dag/chunk.ts';\nimport type {Write} from '../dag/store.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, emptyHash, newRandomHash} from '../hash.ts';\nimport {\n DataNodeImpl,\n type Entry,\n InternalNodeImpl,\n createNewInternalEntryForNode,\n emptyDataNode,\n isDataNodeImpl,\n newNodeImpl,\n partition,\n toChunkData,\n} from './node.ts';\nimport {BTreeRead} from './read.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport class BTreeWrite extends BTreeRead {\n /**\n * This rw lock is used to ensure we do not mutate the btree in parallel. It\n * would be a problem if we didn't have the lock in cases like this:\n *\n * ```ts\n * const p1 = tree.put('a', 0);\n * const p2 = tree.put('b', 1);\n * await p1;\n * await p2;\n * ```\n *\n * because both `p1` and `p2` would start from the old root hash but a put\n * changes the root hash so the two concurrent puts would lead to only one of\n * them actually working, and it is not deterministic which one would finish\n * last.\n */\n readonly #lock = new Lock();\n readonly #modified: Map<Hash, DataNodeImpl | InternalNodeImpl> = new Map();\n\n declare protected _dagRead: Write;\n\n readonly minSize: number;\n readonly maxSize: number;\n\n constructor(\n dagWrite: Write,\n formatVersion: FormatVersion,\n root: Hash = emptyHash,\n minSize = 8 * 1024,\n maxSize = 16 * 1024,\n getEntrySize: <K, V>(k: K, v: V) => number = getSizeOfEntry,\n chunkHeaderSize?: number,\n ) {\n super(dagWrite, formatVersion, root, getEntrySize, chunkHeaderSize);\n\n this.minSize = minSize;\n this.maxSize = maxSize;\n }\n\n #addToModified(node: DataNodeImpl | InternalNodeImpl): void {\n assert(node.isMutable, 'Expected node to be mutable');\n this.#modified.set(node.hash, node);\n this._cache.set(node.hash, node);\n }\n\n updateNode(node: DataNodeImpl | InternalNodeImpl): void {\n assert(node.isMutable, 'Expected node to be mutable');\n this.#modified.delete(node.hash);\n node.hash = newRandomHash();\n this.#addToModified(node);\n }\n\n newInternalNodeImpl(\n entries: Array<Entry<Hash>>,\n level: number,\n ): InternalNodeImpl {\n const n = new InternalNodeImpl(entries, newRandomHash(), level, true);\n this.#addToModified(n);\n return n;\n }\n\n newDataNodeImpl(entries: Entry<FrozenJSONValue>[]): DataNodeImpl {\n const n = new DataNodeImpl(entries, newRandomHash(), true);\n this.#addToModified(n);\n return n;\n }\n\n newNodeImpl(entries: Entry<FrozenJSONValue>[], level: number): DataNodeImpl;\n newNodeImpl(entries: Entry<Hash>[], level: number): InternalNodeImpl;\n newNodeImpl(\n entries: Entry<Hash>[] | Entry<FrozenJSONValue>[],\n level: number,\n ): InternalNodeImpl | DataNodeImpl;\n newNodeImpl(\n entries: Entry<Hash>[] | Entry<FrozenJSONValue>[],\n level: number,\n ): InternalNodeImpl | DataNodeImpl {\n const n = newNodeImpl(entries, newRandomHash(), level, true);\n this.#addToModified(n);\n return n;\n }\n\n put(key: string, value: FrozenJSONValue): Promise<void> {\n return this.#lock.withLock(async () => {\n const oldRootNode = await this.getNode(this.rootHash);\n const entrySize = this.getEntrySize(key, value);\n const rootNode = await oldRootNode.set(key, value, entrySize, this);\n\n // We do the rebalancing in the parent so we need to do it here as well.\n if (rootNode.getChildNodeSize(this) > this.maxSize) {\n const headerSize = this.chunkHeaderSize;\n const {level} = rootNode;\n const entries = partition(\n rootNode.entries,\n value => value[2],\n this.minSize - headerSize,\n this.maxSize - headerSize,\n entries => {\n const node = this.newNodeImpl(entries, level);\n return createNewInternalEntryForNode(node, this.getEntrySize);\n },\n );\n const newRoot = this.newInternalNodeImpl(entries, level + 1);\n this.rootHash = newRoot.hash;\n return;\n }\n\n this.rootHash = rootNode.hash;\n });\n }\n\n del(key: string): Promise<boolean> {\n return this.#lock.withLock(async () => {\n const oldRootNode = await this.getNode(this.rootHash);\n const newRootNode = await oldRootNode.del(key, this);\n\n // No need to rebalance here since if root gets too small there is nothing\n // we can do about that.\n const found = this.rootHash !== newRootNode.hash;\n if (found) {\n // Flatten one layer.\n if (newRootNode.level > 0 && newRootNode.entries.length === 1) {\n this.rootHash = (newRootNode as InternalNodeImpl).entries[0][1];\n } else {\n this.rootHash = newRootNode.hash;\n }\n }\n\n return found;\n });\n }\n\n clear(): Promise<void> {\n return this.#lock.withLock(() => {\n this.#modified.clear();\n this.rootHash = emptyHash;\n });\n }\n\n flush(): Promise<Hash> {\n return this.#lock.withLock(async () => {\n const dagWrite = this._dagRead;\n\n if (this.rootHash === emptyHash) {\n // Write a chunk for the empty tree.\n const chunk = dagWrite.createChunk(emptyDataNode, []);\n await dagWrite.putChunk(chunk as Chunk<ReadonlyJSONValue>);\n return chunk.hash;\n }\n\n const newChunks: Chunk[] = [];\n const newRoot = gatherNewChunks(\n this.rootHash,\n newChunks,\n dagWrite.createChunk,\n this.#modified,\n this._formatVersion,\n );\n await Promise.all(newChunks.map(chunk => dagWrite.putChunk(chunk)));\n this.#modified.clear();\n this.rootHash = newRoot;\n return newRoot;\n });\n }\n}\n\nfunction gatherNewChunks(\n hash: Hash,\n newChunks: Chunk[],\n createChunk: CreateChunk,\n modified: Map<Hash, DataNodeImpl | InternalNodeImpl>,\n formatVersion: FormatVersion,\n): Hash {\n const node = modified.get(hash);\n if (node === undefined) {\n // Not modified, use the original.\n return hash;\n }\n\n if (isDataNodeImpl(node)) {\n const chunk = createChunk(toChunkData(node, formatVersion), []);\n newChunks.push(chunk);\n return chunk.hash;\n }\n\n // The BTree cannot have duplicate keys so the child entry hashes are unique.\n // No need fot a set to dedupe here.\n const refs: Hash[] = [];\n const {entries} = node;\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n const childHash = entry[1];\n const newChildHash = gatherNewChunks(\n childHash,\n newChunks,\n createChunk,\n modified,\n formatVersion,\n );\n if (newChildHash !== childHash) {\n // MUTATES the entries!\n // Hashes do not change the size of the entry because all hashes have the same length\n entries[i] = [entry[0], newChildHash, entry[2]];\n }\n refs.push(newChildHash);\n }\n const chunk = createChunk(toChunkData(node, formatVersion), toRefs(refs));\n newChunks.push(chunk);\n return chunk.hash;\n}\n"],"mappings":"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"}
|
|
@@ -30,6 +30,12 @@ export declare class BTreeSet<K> {
|
|
|
30
30
|
valuesReversed(): IterableIterator<K>;
|
|
31
31
|
valuesFromReversed(highestKey?: K, inclusive?: boolean): IterableIterator<K>;
|
|
32
32
|
[Symbol.iterator](): IterableIterator<K>;
|
|
33
|
+
/**
|
|
34
|
+
* Builds a BTreeSet from a pre-sorted iterable in O(N) by constructing
|
|
35
|
+
* the tree bottom-up. The caller must ensure entries are sorted by
|
|
36
|
+
* `comparator`; violating this produces an invalid tree.
|
|
37
|
+
*/
|
|
38
|
+
static fromSorted<K>(comparator: Comparator<K>, sortedEntries: Iterable<K>): BTreeSet<K>;
|
|
33
39
|
}
|
|
34
40
|
export {};
|
|
35
41
|
//# sourceMappingURL=btree-set.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"btree-set.d.ts","sourceRoot":"","sources":["../../../../shared/src/btree-set.ts"],"names":[],"mappings":"AAIA,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;AAE5C,qBAAa,QAAQ,CAAC,CAAC;;IAErB,IAAI,EAAE,MAAM,CAAK;IAEjB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;gBAEvB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;IASpE,+CAA+C;IAC/C,KAAK;IAKL,KAAK;IAQL,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAI1B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI;IASjB;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIpB;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAyBvB,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAI3B,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAI7B,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,SAAS,GAAE,OAAc,GAAG,gBAAgB,CAAC,CAAC,CAAC;IAIzE,cAAc,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAUrC,kBAAkB,CAChB,UAAU,CAAC,EAAE,CAAC,EACd,SAAS,GAAE,OAAc,GACxB,gBAAgB,CAAC,CAAC,CAAC;IAetB,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"btree-set.d.ts","sourceRoot":"","sources":["../../../../shared/src/btree-set.ts"],"names":[],"mappings":"AAIA,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;AAE5C,qBAAa,QAAQ,CAAC,CAAC;;IAErB,IAAI,EAAE,MAAM,CAAK;IAEjB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;gBAEvB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;IASpE,+CAA+C;IAC/C,KAAK;IAKL,KAAK;IAQL,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAI1B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI;IASjB;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIpB;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAyBvB,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAI3B,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAI7B,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,SAAS,GAAE,OAAc,GAAG,gBAAgB,CAAC,CAAC,CAAC;IAIzE,cAAc,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAUrC,kBAAkB,CAChB,UAAU,CAAC,EAAE,CAAC,EACd,SAAS,GAAE,OAAc,GACxB,gBAAgB,CAAC,CAAC,CAAC;IAetB,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAIxC;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,EACjB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC,GACzB,QAAQ,CAAC,CAAC,CAAC;CA6Cf"}
|
|
@@ -86,6 +86,40 @@ var BTreeSet = class BTreeSet {
|
|
|
86
86
|
[Symbol.iterator]() {
|
|
87
87
|
return this.keys();
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Builds a BTreeSet from a pre-sorted iterable in O(N) by constructing
|
|
91
|
+
* the tree bottom-up. The caller must ensure entries are sorted by
|
|
92
|
+
* `comparator`; violating this produces an invalid tree.
|
|
93
|
+
*/
|
|
94
|
+
static fromSorted(comparator, sortedEntries) {
|
|
95
|
+
const tree = new BTreeSet(comparator);
|
|
96
|
+
const leaves = [];
|
|
97
|
+
let currentKeys = [];
|
|
98
|
+
let size = 0;
|
|
99
|
+
for (const key of sortedEntries) {
|
|
100
|
+
currentKeys.push(key);
|
|
101
|
+
if (currentKeys.length === MAX_NODE_SIZE) {
|
|
102
|
+
leaves.push(new BNode(currentKeys));
|
|
103
|
+
currentKeys = [];
|
|
104
|
+
}
|
|
105
|
+
size++;
|
|
106
|
+
}
|
|
107
|
+
if (currentKeys.length > 0) leaves.push(new BNode(currentKeys));
|
|
108
|
+
if (leaves.length === 0) return tree;
|
|
109
|
+
tree.size = size;
|
|
110
|
+
if (leaves.length === 1) {
|
|
111
|
+
tree.#root = leaves[0];
|
|
112
|
+
return tree;
|
|
113
|
+
}
|
|
114
|
+
let currentLevel = leaves;
|
|
115
|
+
while (currentLevel.length > 1) {
|
|
116
|
+
const nextLevel = [];
|
|
117
|
+
for (let i = 0; i < currentLevel.length; i += MAX_NODE_SIZE) nextLevel.push(new BNodeInternal(currentLevel.slice(i, i + MAX_NODE_SIZE)));
|
|
118
|
+
currentLevel = nextLevel;
|
|
119
|
+
}
|
|
120
|
+
tree.#root = currentLevel[0];
|
|
121
|
+
return tree;
|
|
122
|
+
}
|
|
89
123
|
};
|
|
90
124
|
var BTreeForwardIterator = class {
|
|
91
125
|
#nodeQueue;
|