@verdant-web/server 3.3.10 → 4.0.0

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.
Files changed (204) hide show
  1. package/bin/server.mjs +110 -107
  2. package/dist/esm/TokenVerifier.d.ts +1 -0
  3. package/dist/esm/TokenVerifier.js +3 -0
  4. package/dist/esm/TokenVerifier.js.map +1 -1
  5. package/dist/esm/adapters/node/errorHandler.d.ts +2 -0
  6. package/dist/esm/adapters/node/errorHandler.js +12 -0
  7. package/dist/esm/adapters/node/errorHandler.js.map +1 -0
  8. package/dist/esm/adapters/node/getRequestToken.d.ts +1 -0
  9. package/dist/esm/adapters/node/getRequestToken.js +25 -0
  10. package/dist/esm/adapters/node/getRequestToken.js.map +1 -0
  11. package/dist/esm/adapters/node/httpRouter.d.ts +47 -0
  12. package/dist/esm/adapters/node/httpRouter.js +88 -0
  13. package/dist/esm/adapters/node/httpRouter.js.map +1 -0
  14. package/dist/esm/adapters/node/index.d.ts +5 -0
  15. package/dist/esm/adapters/node/index.js +6 -0
  16. package/dist/esm/adapters/node/index.js.map +1 -0
  17. package/dist/esm/adapters/node/tokenMiddleware.d.ts +7 -0
  18. package/dist/esm/adapters/node/tokenMiddleware.js +22 -0
  19. package/dist/esm/adapters/node/tokenMiddleware.js.map +1 -0
  20. package/dist/esm/adapters/node/websocketHandler.d.ts +4 -0
  21. package/dist/esm/adapters/node/websocketHandler.js +54 -0
  22. package/dist/esm/adapters/node/websocketHandler.js.map +1 -0
  23. package/dist/esm/connections/ClientConnection.d.ts +25 -0
  24. package/dist/esm/{ClientConnection.js → connections/ClientConnection.js} +47 -38
  25. package/dist/esm/connections/ClientConnection.js.map +1 -0
  26. package/dist/esm/connections/MessageSender.d.ts +5 -0
  27. package/dist/esm/connections/MessageSender.js.map +1 -0
  28. package/dist/esm/connections/Presence.d.ts +23 -0
  29. package/dist/esm/connections/Presence.js +65 -0
  30. package/dist/esm/connections/Presence.js.map +1 -0
  31. package/dist/esm/core.d.ts +15 -0
  32. package/dist/esm/core.js +15 -0
  33. package/dist/esm/core.js.map +1 -0
  34. package/dist/esm/defaults.d.ts +2 -0
  35. package/dist/esm/defaults.js +3 -0
  36. package/dist/esm/defaults.js.map +1 -0
  37. package/dist/esm/files/FileStorage.d.ts +12 -3
  38. package/dist/esm/files/FileStorage.js +20 -0
  39. package/dist/esm/files/FileStorage.js.map +1 -1
  40. package/dist/esm/index.d.ts +7 -7
  41. package/dist/esm/index.js +2 -2
  42. package/dist/esm/index.js.map +1 -1
  43. package/dist/esm/internals.d.ts +14 -0
  44. package/dist/esm/internals.js +12 -0
  45. package/dist/esm/internals.js.map +1 -0
  46. package/dist/esm/libraries/Library.d.ts +78 -0
  47. package/dist/esm/{ServerLibrary.js → libraries/Library.js} +153 -144
  48. package/dist/esm/libraries/Library.js.map +1 -0
  49. package/dist/esm/{ServerLibrary.test.js → libraries/Library.test.js} +42 -33
  50. package/dist/esm/libraries/Library.test.js.map +1 -0
  51. package/dist/esm/libraries/generic.d.ts +8 -0
  52. package/dist/esm/libraries/generic.js +2 -0
  53. package/dist/esm/libraries/generic.js.map +1 -0
  54. package/dist/esm/libraries/singleNode.d.ts +53 -0
  55. package/dist/esm/libraries/singleNode.js +88 -0
  56. package/dist/esm/libraries/singleNode.js.map +1 -0
  57. package/dist/esm/logger.d.ts +1 -0
  58. package/dist/esm/logger.js +2 -0
  59. package/dist/esm/logger.js.map +1 -0
  60. package/dist/esm/storage/Storage.d.ts +39 -38
  61. package/dist/esm/storage/StorageManager.d.ts +11 -0
  62. package/dist/esm/storage/StorageManager.js +32 -0
  63. package/dist/esm/storage/StorageManager.js.map +1 -0
  64. package/dist/esm/storage/index.d.ts +1 -2
  65. package/dist/esm/storage/index.js +1 -2
  66. package/dist/esm/storage/index.js.map +1 -1
  67. package/dist/esm/storage/sqlShard/Databases.d.ts +5 -3
  68. package/dist/esm/storage/sqlShard/Databases.js +11 -7
  69. package/dist/esm/storage/sqlShard/Databases.js.map +1 -1
  70. package/dist/esm/storage/sqlShard/SqlBaselines.d.ts +16 -17
  71. package/dist/esm/storage/sqlShard/SqlBaselines.js +55 -59
  72. package/dist/esm/storage/sqlShard/SqlBaselines.js.map +1 -1
  73. package/dist/esm/storage/sqlShard/SqlFileMetadata.d.ts +13 -13
  74. package/dist/esm/storage/sqlShard/SqlFileMetadata.js +44 -57
  75. package/dist/esm/storage/sqlShard/SqlFileMetadata.js.map +1 -1
  76. package/dist/esm/storage/sqlShard/SqlOperations.d.ts +12 -12
  77. package/dist/esm/storage/sqlShard/SqlOperations.js +38 -73
  78. package/dist/esm/storage/sqlShard/SqlOperations.js.map +1 -1
  79. package/dist/esm/storage/sqlShard/SqlReplicas.d.ts +18 -17
  80. package/dist/esm/storage/sqlShard/SqlReplicas.js +63 -100
  81. package/dist/esm/storage/sqlShard/SqlReplicas.js.map +1 -1
  82. package/dist/esm/storage/sqlShard/database.d.ts +25 -5
  83. package/dist/esm/storage/sqlShard/database.js +47 -15
  84. package/dist/esm/storage/sqlShard/database.js.map +1 -1
  85. package/dist/esm/storage/sqlShard/migrations/v1.d.ts +2 -3
  86. package/dist/esm/storage/sqlShard/migrations/v1.js +42 -47
  87. package/dist/esm/storage/sqlShard/migrations/v1.js.map +1 -1
  88. package/dist/esm/storage/sqlShard/migrations/v2.d.ts +2 -3
  89. package/dist/esm/storage/sqlShard/migrations/v2.js +5 -15
  90. package/dist/esm/storage/sqlShard/migrations/v2.js.map +1 -1
  91. package/dist/esm/storage/sqlShard/migrations.d.ts +5 -7
  92. package/dist/esm/storage/sqlShard/migrations.js +48 -1
  93. package/dist/esm/storage/sqlShard/migrations.js.map +1 -1
  94. package/dist/esm/storage/sqlShard/sqlShardStorage.d.ts +25 -4
  95. package/dist/esm/storage/sqlShard/sqlShardStorage.js +43 -40
  96. package/dist/esm/storage/sqlShard/sqlShardStorage.js.map +1 -1
  97. package/dist/esm/storage/sqlShard/tables.d.ts +9 -6
  98. package/dist/esm/types.d.ts +1 -2
  99. package/package.json +24 -13
  100. package/src/TokenVerifier.ts +4 -0
  101. package/src/adapters/node/errorHandler.ts +18 -0
  102. package/src/adapters/node/getRequestToken.ts +34 -0
  103. package/src/adapters/node/httpRouter.ts +139 -0
  104. package/src/adapters/node/index.ts +5 -0
  105. package/src/adapters/node/tokenMiddleware.ts +32 -0
  106. package/src/adapters/node/websocketHandler.ts +79 -0
  107. package/src/{ClientConnection.ts → connections/ClientConnection.ts} +64 -57
  108. package/src/connections/MessageSender.ts +6 -0
  109. package/src/connections/Presence.ts +95 -0
  110. package/src/core.ts +36 -0
  111. package/src/defaults.ts +2 -0
  112. package/src/files/FileStorage.ts +36 -3
  113. package/src/index.ts +7 -7
  114. package/src/internals.ts +14 -0
  115. package/src/{ServerLibrary.test.ts → libraries/Library.test.ts} +51 -49
  116. package/src/{ServerLibrary.ts → libraries/Library.ts} +203 -206
  117. package/src/libraries/generic.ts +9 -0
  118. package/src/libraries/singleNode.ts +145 -0
  119. package/src/logger.ts +4 -0
  120. package/src/storage/Storage.ts +37 -66
  121. package/src/storage/StorageManager.ts +39 -0
  122. package/src/storage/index.ts +1 -2
  123. package/src/storage/sqlShard/Databases.ts +26 -13
  124. package/src/storage/sqlShard/SqlBaselines.ts +58 -67
  125. package/src/storage/sqlShard/SqlFileMetadata.ts +51 -72
  126. package/src/storage/sqlShard/SqlOperations.ts +57 -81
  127. package/src/storage/sqlShard/SqlReplicas.ts +80 -121
  128. package/src/storage/sqlShard/database.ts +82 -18
  129. package/src/storage/sqlShard/migrations/v1.ts +42 -52
  130. package/src/storage/sqlShard/migrations/v2.ts +5 -19
  131. package/src/storage/sqlShard/migrations.ts +66 -1
  132. package/src/storage/sqlShard/sqlShardStorage.ts +56 -55
  133. package/src/storage/sqlShard/tables.ts +6 -6
  134. package/src/types.ts +1 -2
  135. package/dist/esm/ClientConnection.d.ts +0 -19
  136. package/dist/esm/ClientConnection.js.map +0 -1
  137. package/dist/esm/MessageSender.d.ts +0 -5
  138. package/dist/esm/MessageSender.js.map +0 -1
  139. package/dist/esm/Presence.d.ts +0 -18
  140. package/dist/esm/Presence.js +0 -65
  141. package/dist/esm/Presence.js.map +0 -1
  142. package/dist/esm/ReplicaKeepaliveTimers.d.ts +0 -17
  143. package/dist/esm/ReplicaKeepaliveTimers.js +0 -27
  144. package/dist/esm/ReplicaKeepaliveTimers.js.map +0 -1
  145. package/dist/esm/Server.d.ts +0 -166
  146. package/dist/esm/Server.js +0 -572
  147. package/dist/esm/Server.js.map +0 -1
  148. package/dist/esm/ServerLibrary.d.ts +0 -64
  149. package/dist/esm/ServerLibrary.js.map +0 -1
  150. package/dist/esm/ServerLibrary.test.js.map +0 -1
  151. package/dist/esm/storage/sql/SqlBaselines.d.ts +0 -22
  152. package/dist/esm/storage/sql/SqlBaselines.js +0 -107
  153. package/dist/esm/storage/sql/SqlBaselines.js.map +0 -1
  154. package/dist/esm/storage/sql/SqlFileMetadata.d.ts +0 -18
  155. package/dist/esm/storage/sql/SqlFileMetadata.js +0 -69
  156. package/dist/esm/storage/sql/SqlFileMetadata.js.map +0 -1
  157. package/dist/esm/storage/sql/SqlOperations.d.ts +0 -19
  158. package/dist/esm/storage/sql/SqlOperations.js +0 -122
  159. package/dist/esm/storage/sql/SqlOperations.js.map +0 -1
  160. package/dist/esm/storage/sql/SqlReplicas.d.ts +0 -32
  161. package/dist/esm/storage/sql/SqlReplicas.js +0 -163
  162. package/dist/esm/storage/sql/SqlReplicas.js.map +0 -1
  163. package/dist/esm/storage/sql/database.d.ts +0 -9
  164. package/dist/esm/storage/sql/database.js +0 -24
  165. package/dist/esm/storage/sql/database.js.map +0 -1
  166. package/dist/esm/storage/sql/migrations/v1.d.ts +0 -3
  167. package/dist/esm/storage/sql/migrations/v1.js +0 -59
  168. package/dist/esm/storage/sql/migrations/v1.js.map +0 -1
  169. package/dist/esm/storage/sql/migrations/v2.d.ts +0 -3
  170. package/dist/esm/storage/sql/migrations/v2.js +0 -16
  171. package/dist/esm/storage/sql/migrations/v2.js.map +0 -1
  172. package/dist/esm/storage/sql/migrations.d.ts +0 -7
  173. package/dist/esm/storage/sql/migrations.js +0 -4
  174. package/dist/esm/storage/sql/migrations.js.map +0 -1
  175. package/dist/esm/storage/sql/sqlStorage.d.ts +0 -6
  176. package/dist/esm/storage/sql/sqlStorage.js +0 -35
  177. package/dist/esm/storage/sql/sqlStorage.js.map +0 -1
  178. package/dist/esm/storage/sql/tables.d.ts +0 -44
  179. package/dist/esm/storage/sql/tables.js +0 -2
  180. package/dist/esm/storage/sql/tables.js.map +0 -1
  181. package/dist/esm/storage/sqlShard/transfer.d.ts +0 -7
  182. package/dist/esm/storage/sqlShard/transfer.js +0 -90
  183. package/dist/esm/storage/sqlShard/transfer.js.map +0 -1
  184. package/dist/esm/storage/sqlShard/transfer.test.d.ts +0 -1
  185. package/dist/esm/storage/sqlShard/transfer.test.js +0 -109
  186. package/dist/esm/storage/sqlShard/transfer.test.js.map +0 -1
  187. package/src/MessageSender.ts +0 -10
  188. package/src/Presence.ts +0 -86
  189. package/src/ReplicaKeepaliveTimers.ts +0 -34
  190. package/src/Server.ts +0 -859
  191. package/src/storage/sql/SqlBaselines.ts +0 -147
  192. package/src/storage/sql/SqlFileMetadata.ts +0 -94
  193. package/src/storage/sql/SqlOperations.ts +0 -160
  194. package/src/storage/sql/SqlReplicas.ts +0 -234
  195. package/src/storage/sql/database.ts +0 -32
  196. package/src/storage/sql/migrations/v1.ts +0 -64
  197. package/src/storage/sql/migrations/v2.ts +0 -20
  198. package/src/storage/sql/migrations.ts +0 -4
  199. package/src/storage/sql/sqlStorage.ts +0 -57
  200. package/src/storage/sql/tables.ts +0 -51
  201. package/src/storage/sqlShard/transfer.test.ts +0 -149
  202. package/src/storage/sqlShard/transfer.ts +0 -123
  203. /package/dist/esm/{MessageSender.js → connections/MessageSender.js} +0 -0
  204. /package/dist/esm/{ServerLibrary.test.d.ts → libraries/Library.test.d.ts} +0 -0
@@ -1,64 +0,0 @@
1
- import { Kysely } from 'kysely';
2
-
3
- export async function up(db: Kysely<any>) {
4
- await db.schema
5
- .createTable('DocumentBaseline')
6
- .ifNotExists()
7
- .addColumn('oid', 'text')
8
- .addColumn('snapshot', 'text')
9
- .addColumn('timestamp', 'text', (cb) => cb.notNull())
10
- .addColumn('libraryId', 'text', (cb) => cb.notNull())
11
- .addPrimaryKeyConstraint('DocumentBaseline_primaryKey', [
12
- 'libraryId',
13
- 'oid',
14
- ])
15
- .execute();
16
-
17
- await db.schema
18
- .createTable('OperationHistory')
19
- .ifNotExists()
20
- .addColumn('oid', 'text', (cb) => cb.notNull())
21
- .addColumn('timestamp', 'text', (cb) => cb.notNull())
22
- .addColumn('data', 'text', (cb) => cb.notNull())
23
- .addColumn('serverOrder', 'integer', (cb) => cb.notNull().defaultTo(0))
24
- .addColumn('replicaId', 'text', (cb) => cb.notNull())
25
- .addColumn('libraryId', 'text', (cb) => cb.notNull())
26
- .addPrimaryKeyConstraint('OperationHistory_primaryKey', [
27
- 'libraryId',
28
- 'replicaId',
29
- 'oid',
30
- 'timestamp',
31
- ])
32
- .execute();
33
-
34
- await db.schema
35
- .createTable('ReplicaInfo')
36
- .ifNotExists()
37
- .addColumn('id', 'text')
38
- .addColumn('libraryId', 'text', (cb) => cb.notNull())
39
- .addColumn('clientId', 'text', (cb) => cb.notNull())
40
- .addColumn('lastSeenWallClockTime', 'integer')
41
- .addColumn('ackedLogicalTime', 'text')
42
- .addColumn('type', 'integer', (cb) => cb.notNull().defaultTo(0))
43
- .addColumn('ackedServerOrder', 'integer', (cb) => cb.notNull().defaultTo(0))
44
- .addPrimaryKeyConstraint('ReplicaInfo_primaryKey', ['libraryId', 'id'])
45
- .execute();
46
-
47
- await db.schema
48
- .createTable('FileMetadata')
49
- .ifNotExists()
50
- .addColumn('libraryId', 'text', (cb) => cb.notNull())
51
- .addColumn('fileId', 'text', (cb) => cb.notNull())
52
- .addColumn('name', 'text', (cb) => cb.notNull())
53
- .addColumn('type', 'text', (cb) => cb.notNull())
54
- .addColumn('pendingDeleteAt', 'integer')
55
- .addPrimaryKeyConstraint('FileMetadata_primaryKey', ['libraryId', 'fileId'])
56
- .execute();
57
- }
58
-
59
- export async function down(db: Kysely<any>) {
60
- await db.schema.dropTable('DocumentBaseline').execute();
61
- await db.schema.dropTable('OperationHistory').execute();
62
- await db.schema.dropTable('ReplicaInfo').execute();
63
- await db.schema.dropTable('FileMetadata').execute();
64
- }
@@ -1,20 +0,0 @@
1
- import { Kysely } from 'kysely';
2
-
3
- export async function up(db: Kysely<any>) {
4
- // add authz column to OperationHistory and DocumentBaseline
5
- await db.schema
6
- .alterTable('OperationHistory')
7
- .addColumn('authz', 'text', (cb) => cb.defaultTo(null))
8
- .execute();
9
-
10
- await db.schema
11
- .alterTable('DocumentBaseline')
12
- .addColumn('authz', 'text', (cb) => cb.defaultTo(null))
13
- .execute();
14
- }
15
-
16
- export async function down(db: Kysely<any>) {
17
- await db.schema.alterTable('OperationHistory').dropColumn('authz').execute();
18
-
19
- await db.schema.alterTable('DocumentBaseline').dropColumn('authz').execute();
20
- }
@@ -1,4 +0,0 @@
1
- import * as v1 from './migrations/v1.js';
2
- import * as v2 from './migrations/v2.js';
3
-
4
- export default { v1, v2 };
@@ -1,57 +0,0 @@
1
- import Database from 'better-sqlite3';
2
- import { Kysely, SqliteDialect } from 'kysely';
3
- import { StorageFactory } from '../Storage.js';
4
- import { SqlBaselines } from './SqlBaselines.js';
5
- import { SqlOperations } from './SqlOperations.js';
6
- import { SqlReplicas } from './SqlReplicas.js';
7
- import { Database as DatabaseTypes } from './tables.js';
8
- import { SqlFileMetadata } from './SqlFileMetadata.js';
9
- import { migrateToLatest } from '@a-type/kysely';
10
- import migrations from './migrations.js';
11
- import { openDatabase } from './database.js';
12
-
13
- export const sqlStorage = ({
14
- databaseFile: dbFile,
15
- skipMigrations,
16
- disableWal,
17
- }: {
18
- databaseFile: string;
19
- skipMigrations?: boolean;
20
- disableWal?: boolean;
21
- }): StorageFactory => {
22
- const { db, ready } = openDatabase(dbFile, {
23
- skipMigrations,
24
- disableWal,
25
- });
26
- return (options) => {
27
- const baselines = new SqlBaselines(db, 'sqlite');
28
- const operations = new SqlOperations(db, 'sqlite');
29
- const replicas = new SqlReplicas(
30
- db,
31
- options.replicaTruancyMinutes,
32
- 'sqlite',
33
- );
34
- const fileMetadata = new SqlFileMetadata(
35
- db,
36
- options.fileDeleteExpirationDays,
37
- 'sqlite',
38
- );
39
- const close = async () => {
40
- storage.open = false;
41
- await db.destroy();
42
- };
43
- const storage = {
44
- baselines,
45
- operations,
46
- replicas,
47
- fileMetadata,
48
- close,
49
- open: false,
50
- ready,
51
- };
52
- ready.then(() => {
53
- storage.open = true;
54
- });
55
- return storage;
56
- };
57
- };
@@ -1,51 +0,0 @@
1
- import { ReplicaType } from '@verdant-web/common';
2
- import { Generated, Selectable } from 'kysely';
3
-
4
- export interface Database {
5
- OperationHistory: OperationHistoryTable;
6
- DocumentBaseline: DocumentBaselineTable;
7
- ReplicaInfo: ReplicaInfoTable;
8
- FileMetadata: FileMetadataTable;
9
- }
10
-
11
- export interface OperationHistoryTable {
12
- oid: string;
13
- timestamp: string;
14
- data: string;
15
- serverOrder: number;
16
- replicaId: string;
17
- libraryId: string;
18
- authz: string | null;
19
- }
20
- export type OperationHistoryRow = Selectable<OperationHistoryTable>;
21
-
22
- export interface DocumentBaselineTable {
23
- oid: string;
24
- snapshot: string;
25
- timestamp: string;
26
- libraryId: string;
27
- authz: string | null;
28
- }
29
- export type DocumentBaselineRow = Selectable<DocumentBaselineTable>;
30
-
31
- export interface ReplicaInfoTable {
32
- id: string;
33
- libraryId: string;
34
- clientId: string;
35
- lastSeenWallClockTime: number | null;
36
- ackedLogicalTime: string | null;
37
- type: ReplicaType;
38
- ackedServerOrder: Generated<number>;
39
- }
40
-
41
- export type ReplicaInfoRow = Selectable<ReplicaInfoTable>;
42
-
43
- export interface FileMetadataTable {
44
- libraryId: string;
45
- fileId: string;
46
- name: string;
47
- type: string;
48
- pendingDeleteAt: number | null;
49
- }
50
-
51
- export type FileMetadataRow = Selectable<FileMetadataTable>;
@@ -1,149 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
- import { openDatabase as openUnifiedDatabase } from '../sql/database.js';
3
- import {
4
- randomBaselines,
5
- randomFileMetadata,
6
- randomOperations,
7
- randomReplicaInfo,
8
- } from './_testData/unifiedData.js';
9
- import { join, dirname } from 'path';
10
- import { transferToShards } from './transfer.js';
11
- import { rm, mkdir } from 'fs/promises';
12
- import { fileURLToPath } from 'url';
13
- import {
14
- DocumentBaselineRow,
15
- FileMetadataRow,
16
- OperationHistoryRow,
17
- ReplicaInfoRow,
18
- } from '../sql/tables.js';
19
- import { Kysely } from 'kysely';
20
-
21
- const testTempDir = join(
22
- dirname(fileURLToPath(import.meta.url)),
23
- '_testData',
24
- 'tmp',
25
- );
26
-
27
- describe('sql-shard storage transfer utility', () => {
28
- const databases = new Array<Kysely<any>>();
29
- beforeAll(async () => {
30
- try {
31
- await rm(testTempDir, { recursive: true });
32
- } catch (err) {}
33
- // create a temp dir
34
- await mkdir(testTempDir, { recursive: true });
35
- });
36
- afterAll(async () => {
37
- try {
38
- await Promise.all(databases.map((db) => db.destroy()));
39
- // empty the temp dir
40
- await rm(testTempDir, { recursive: true });
41
- } catch (err) {
42
- // what can you do...
43
- console.error(err);
44
- }
45
- });
46
-
47
- it('should transfer data from unified database to shards', async () => {
48
- const unifiedFile = join(testTempDir, 'unified.sqlite');
49
- const { db: unifiedDb, ready } = openUnifiedDatabase(unifiedFile);
50
- databases.push(unifiedDb);
51
- await ready;
52
- const libraryIds = ['library-1', 'library-2', 'library-3'];
53
- const randomData = {} as Record<
54
- string,
55
- {
56
- operations: OperationHistoryRow[];
57
- baselines: DocumentBaselineRow[];
58
- replicas: ReplicaInfoRow[];
59
- fileMetadata: FileMetadataRow[];
60
- }
61
- >;
62
- for (const libraryId of libraryIds) {
63
- randomData[libraryId] = {
64
- // test a lot of them - more than max sqlite var count.
65
- // only doing operations here to reduce test time.
66
- operations: randomOperations(libraryId, 6000),
67
- baselines: randomBaselines(libraryId),
68
- replicas: randomReplicaInfo(libraryId),
69
- fileMetadata: randomFileMetadata(libraryId),
70
- };
71
-
72
- // have to batch these...
73
- for (let i = 0; i < randomData[libraryId].operations.length; i += 1000) {
74
- const toInsert = randomData[libraryId].operations.slice(i, i + 1000);
75
- await unifiedDb
76
- .insertInto('OperationHistory')
77
- .values(toInsert)
78
- .execute();
79
- }
80
- for (let i = 0; i < randomData[libraryId].baselines.length; i += 1000) {
81
- const toInsert = randomData[libraryId].baselines.slice(i, i + 1000);
82
- await unifiedDb
83
- .insertInto('DocumentBaseline')
84
- .values(toInsert)
85
- .execute();
86
- }
87
- for (
88
- let i = 0;
89
- i < randomData[libraryId].fileMetadata.length;
90
- i += 1000
91
- ) {
92
- const toInsert = randomData[libraryId].fileMetadata.slice(i, i + 1000);
93
- await unifiedDb.insertInto('FileMetadata').values(toInsert).execute();
94
- }
95
- for (let i = 0; i < randomData[libraryId].replicas.length; i += 1000) {
96
- const toInsert = randomData[libraryId].replicas.slice(i, i + 1000);
97
- await unifiedDb.insertInto('ReplicaInfo').values(toInsert).execute();
98
- }
99
- }
100
-
101
- const shards = await transferToShards({
102
- directory: testTempDir,
103
- file: unifiedFile,
104
- });
105
- Object.values(shards).map((db) => databases.push(db));
106
-
107
- expect(Object.keys(shards).sort()).toEqual(libraryIds.sort());
108
-
109
- for (const [libraryId, db] of Object.entries(shards)) {
110
- const operations = await db
111
- .selectFrom('OperationHistory')
112
- .selectAll()
113
- .execute();
114
- const baselines = await db
115
- .selectFrom('DocumentBaseline')
116
- .selectAll()
117
- .execute();
118
- const replicas = await db.selectFrom('ReplicaInfo').selectAll().execute();
119
- const fileMetadata = await db
120
- .selectFrom('FileMetadata')
121
- .selectAll()
122
- .execute();
123
-
124
- expect(operations.length).toBe(randomData[libraryId].operations.length);
125
- expect(baselines.length).toBe(randomData[libraryId].baselines.length);
126
- expect(replicas.length).toBe(randomData[libraryId].replicas.length);
127
- expect(fileMetadata.length).toBe(
128
- randomData[libraryId].fileMetadata.length,
129
- );
130
-
131
- for (const { oid } of randomData[libraryId].operations) {
132
- expect(operations.find((op) => op.oid === oid)).toBeDefined();
133
- }
134
- for (const { oid } of randomData[libraryId].baselines) {
135
- expect(
136
- baselines.find((baseline) => baseline.oid === oid),
137
- ).toBeDefined();
138
- }
139
- for (const { id } of randomData[libraryId].replicas) {
140
- expect(replicas.find((replica) => replica.id === id)).toBeDefined();
141
- }
142
- for (const { fileId } of randomData[libraryId].fileMetadata) {
143
- expect(
144
- fileMetadata.find((file) => file.fileId === fileId),
145
- ).toBeDefined();
146
- }
147
- }
148
- });
149
- });
@@ -1,123 +0,0 @@
1
- import { Kysely } from 'kysely';
2
- import { openDatabase as openUnifiedDatabase } from '../sql/database.js';
3
- import { Database as UnifiedDatabase } from '../sql/tables.js';
4
- import { Database } from './tables.js';
5
- import { openDatabase } from './database.js';
6
-
7
- const DEFAULT_BATCH_SIZE = 1000;
8
-
9
- export async function transferToShards({
10
- file,
11
- directory,
12
- batchSize = DEFAULT_BATCH_SIZE,
13
- }: {
14
- file: string;
15
- directory: string;
16
- batchSize?: number;
17
- }) {
18
- console.info(
19
- `Transferring from unified database (${file}) to shards (${directory})...`,
20
- );
21
- const { db: unifiedDb, ready } = openUnifiedDatabase(file);
22
- await ready;
23
- const libraries = await unifiedDb
24
- .selectFrom('ReplicaInfo')
25
- .select('ReplicaInfo.libraryId')
26
- .distinct()
27
- .execute();
28
-
29
- const dbs = await Promise.all(
30
- libraries.map(async ({ libraryId }) => {
31
- const db = await openDatabase(directory, libraryId);
32
- await copyLibrary(libraryId, unifiedDb, db, batchSize);
33
- return db;
34
- }),
35
- );
36
-
37
- return libraries.reduce(
38
- (acc, { libraryId }, i) => {
39
- acc[libraryId] = dbs[i];
40
- return acc;
41
- },
42
- {} as Record<string, Kysely<Database>>,
43
- );
44
- }
45
-
46
- async function copyLibrary(
47
- libraryId: string,
48
- source: Kysely<UnifiedDatabase>,
49
- dest: Kysely<Database>,
50
- batchSize: number,
51
- ) {
52
- const operations = await source
53
- .selectFrom('OperationHistory')
54
- .where('libraryId', '=', libraryId)
55
- .selectAll()
56
- .execute();
57
- const baselines = await source
58
- .selectFrom('DocumentBaseline')
59
- .where('libraryId', '=', libraryId)
60
- .selectAll()
61
- .execute();
62
- const replicas = await source
63
- .selectFrom('ReplicaInfo')
64
- .where('libraryId', '=', libraryId)
65
- .selectAll()
66
- .execute();
67
- const fileMetadata = await source
68
- .selectFrom('FileMetadata')
69
- .where('libraryId', '=', libraryId)
70
- .selectAll()
71
- .execute();
72
-
73
- for (const operation of operations) {
74
- // @ts-expect-error
75
- delete operation.libraryId;
76
- }
77
- for (const baseline of baselines) {
78
- // @ts-expect-error
79
- delete baseline.libraryId;
80
- }
81
- for (const replica of replicas) {
82
- // @ts-expect-error
83
- delete replica.libraryId;
84
- }
85
- for (const metadata of fileMetadata) {
86
- // @ts-expect-error
87
- delete metadata.libraryId;
88
- }
89
-
90
- await dest.transaction().execute(async (trx) => {
91
- const actions: Promise<any>[] = [];
92
- // batch insert data to avoid maximum vars limits
93
- if (operations.length) {
94
- for (let i = 0; i < operations.length; i += batchSize) {
95
- const toInsert = operations.slice(i, i + batchSize);
96
- actions.push(
97
- trx.insertInto('OperationHistory').values(toInsert).execute(),
98
- );
99
- }
100
- }
101
- if (baselines.length) {
102
- for (let i = 0; i < baselines.length; i += batchSize) {
103
- const toInsert = baselines.slice(i, i + batchSize);
104
- actions.push(
105
- trx.insertInto('DocumentBaseline').values(toInsert).execute(),
106
- );
107
- }
108
- }
109
- if (replicas.length) {
110
- for (let i = 0; i < replicas.length; i += batchSize) {
111
- const toInsert = replicas.slice(i, i + batchSize);
112
- actions.push(trx.insertInto('ReplicaInfo').values(toInsert).execute());
113
- }
114
- }
115
- if (fileMetadata.length) {
116
- for (let i = 0; i < fileMetadata.length; i += batchSize) {
117
- const toInsert = fileMetadata.slice(i, i + batchSize);
118
- actions.push(trx.insertInto('FileMetadata').values(toInsert).execute());
119
- }
120
- }
121
- await Promise.all(actions);
122
- });
123
- }