prisma-sharding 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +223 -27
- package/dist/cli/migrate.js +117 -0
- package/dist/cli/studio.js +112 -0
- package/dist/cli/test.js +401 -0
- package/dist/index.js +101 -101
- package/dist/index.mjs +101 -101
- package/package.json +17 -3
package/dist/index.mjs
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
1
|
+
// src/utils/index.ts
|
|
2
|
+
function hashString(str) {
|
|
3
|
+
let hash = 0;
|
|
4
|
+
for (let i = 0; i < str.length; i++) {
|
|
5
|
+
const char = str.charCodeAt(i);
|
|
6
|
+
hash = (hash << 5) - hash + char;
|
|
7
|
+
hash = hash & hash;
|
|
8
|
+
}
|
|
9
|
+
return Math.abs(hash);
|
|
10
|
+
}
|
|
11
|
+
function validateUrl(url) {
|
|
12
|
+
return url.startsWith("postgresql://") || url.startsWith("postgres://");
|
|
13
|
+
}
|
|
14
|
+
function createDefaultLogger() {
|
|
15
|
+
return {
|
|
16
|
+
info: (msg) => console.log(`[PrismaSharding] ${msg}`),
|
|
17
|
+
warn: (msg) => console.warn(`[PrismaSharding] ${msg}`),
|
|
18
|
+
error: (msg) => console.error(`[PrismaSharding] ${msg}`)
|
|
19
|
+
};
|
|
20
|
+
}
|
|
20
21
|
|
|
21
22
|
// src/core/errors.ts
|
|
22
23
|
var ShardingError = class _ShardingError extends Error {
|
|
@@ -50,6 +51,87 @@ var RoutingError = class _RoutingError extends ShardingError {
|
|
|
50
51
|
}
|
|
51
52
|
};
|
|
52
53
|
|
|
54
|
+
// src/constants/index.ts
|
|
55
|
+
var DEFAULTS = {
|
|
56
|
+
POOL_MAX_CONNECTIONS: 10,
|
|
57
|
+
POOL_IDLE_TIMEOUT_MS: 1e4,
|
|
58
|
+
POOL_CONNECTION_TIMEOUT_MS: 5e3,
|
|
59
|
+
HEALTH_CHECK_INTERVAL_MS: 3e4,
|
|
60
|
+
CIRCUIT_BREAKER_THRESHOLD: 3,
|
|
61
|
+
CONSISTENT_HASH_VIRTUAL_NODES: 150
|
|
62
|
+
};
|
|
63
|
+
var ERROR_MESSAGES = {
|
|
64
|
+
NO_SHARDS: "At least one shard must be configured",
|
|
65
|
+
SHARD_NOT_FOUND: (id) => `Shard "${id}" not found`,
|
|
66
|
+
NO_HEALTHY_SHARDS: "No healthy shards available",
|
|
67
|
+
INVALID_STRATEGY: (s) => `Invalid routing strategy: "${s}". Use "modulo" or "consistent-hash"`,
|
|
68
|
+
NOT_CONNECTED: "Sharding not connected. Call connect() first",
|
|
69
|
+
ALREADY_CONNECTED: "Sharding already connected",
|
|
70
|
+
MISSING_CLIENT_FACTORY: "createClient function is required",
|
|
71
|
+
INVALID_SHARD_URL: (id) => `Invalid or missing URL for shard "${id}"`
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/core/router.ts
|
|
75
|
+
var ShardRouter = class {
|
|
76
|
+
constructor(config) {
|
|
77
|
+
this.consistentHashRing = /* @__PURE__ */ new Map();
|
|
78
|
+
this.virtualNodes = DEFAULTS.CONSISTENT_HASH_VIRTUAL_NODES;
|
|
79
|
+
this.strategy = config.strategy;
|
|
80
|
+
this.shardIds = config.shardIds;
|
|
81
|
+
this.logger = config.logger;
|
|
82
|
+
if (this.strategy === "consistent-hash") {
|
|
83
|
+
this.initializeConsistentHashRing();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
initializeConsistentHashRing() {
|
|
87
|
+
for (const shardId of this.shardIds) {
|
|
88
|
+
for (let i = 0; i < this.virtualNodes; i++) {
|
|
89
|
+
const hash = hashString(`${shardId}:${i}`);
|
|
90
|
+
this.consistentHashRing.set(hash, shardId);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
getShardIndex(key) {
|
|
95
|
+
const shardCount = this.shardIds.length;
|
|
96
|
+
if (shardCount === 0) {
|
|
97
|
+
throw new RoutingError("No shards available");
|
|
98
|
+
}
|
|
99
|
+
if (this.strategy === "consistent-hash") {
|
|
100
|
+
return this.getIndexConsistentHash(key);
|
|
101
|
+
}
|
|
102
|
+
return this.getIndexModulo(key, shardCount);
|
|
103
|
+
}
|
|
104
|
+
getIndexModulo(key, shardCount) {
|
|
105
|
+
const hash = hashString(key);
|
|
106
|
+
return hash % shardCount;
|
|
107
|
+
}
|
|
108
|
+
getIndexConsistentHash(key) {
|
|
109
|
+
const hash = hashString(key);
|
|
110
|
+
const sortedHashes = Array.from(this.consistentHashRing.keys()).sort((a, b) => a - b);
|
|
111
|
+
for (const ringHash of sortedHashes) {
|
|
112
|
+
if (hash <= ringHash) {
|
|
113
|
+
const shardId = this.consistentHashRing.get(ringHash);
|
|
114
|
+
const match2 = shardId.match(/shard_(\d+)/);
|
|
115
|
+
return match2 ? parseInt(match2[1], 10) - 1 : 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const firstShardId = this.consistentHashRing.get(sortedHashes[0]);
|
|
119
|
+
const match = firstShardId.match(/shard_(\d+)/);
|
|
120
|
+
return match ? parseInt(match[1], 10) - 1 : 0;
|
|
121
|
+
}
|
|
122
|
+
getShardId(key) {
|
|
123
|
+
const index = this.getShardIndex(key);
|
|
124
|
+
return this.shardIds[index] || `shard_${index + 1}`;
|
|
125
|
+
}
|
|
126
|
+
getRandomShardIndex() {
|
|
127
|
+
return Math.floor(Math.random() * this.shardIds.length);
|
|
128
|
+
}
|
|
129
|
+
getRandomShardId() {
|
|
130
|
+
const index = this.getRandomShardIndex();
|
|
131
|
+
return this.shardIds[index];
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
53
135
|
// src/core/manager.ts
|
|
54
136
|
var ShardManager = class {
|
|
55
137
|
constructor(config) {
|
|
@@ -217,88 +299,6 @@ var ShardManager = class {
|
|
|
217
299
|
}
|
|
218
300
|
};
|
|
219
301
|
|
|
220
|
-
// src/utils/index.ts
|
|
221
|
-
function hashString(str) {
|
|
222
|
-
let hash = 0;
|
|
223
|
-
for (let i = 0; i < str.length; i++) {
|
|
224
|
-
const char = str.charCodeAt(i);
|
|
225
|
-
hash = (hash << 5) - hash + char;
|
|
226
|
-
hash = hash & hash;
|
|
227
|
-
}
|
|
228
|
-
return Math.abs(hash);
|
|
229
|
-
}
|
|
230
|
-
function validateUrl(url) {
|
|
231
|
-
return url.startsWith("postgresql://") || url.startsWith("postgres://");
|
|
232
|
-
}
|
|
233
|
-
function createDefaultLogger() {
|
|
234
|
-
return {
|
|
235
|
-
info: (msg) => console.log(`[PrismaSharding] ${msg}`),
|
|
236
|
-
warn: (msg) => console.warn(`[PrismaSharding] ${msg}`),
|
|
237
|
-
error: (msg) => console.error(`[PrismaSharding] ${msg}`)
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// src/core/router.ts
|
|
242
|
-
var ShardRouter = class {
|
|
243
|
-
constructor(config) {
|
|
244
|
-
this.consistentHashRing = /* @__PURE__ */ new Map();
|
|
245
|
-
this.virtualNodes = DEFAULTS.CONSISTENT_HASH_VIRTUAL_NODES;
|
|
246
|
-
this.strategy = config.strategy;
|
|
247
|
-
this.shardIds = config.shardIds;
|
|
248
|
-
this.logger = config.logger;
|
|
249
|
-
if (this.strategy === "consistent-hash") {
|
|
250
|
-
this.initializeConsistentHashRing();
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
initializeConsistentHashRing() {
|
|
254
|
-
for (const shardId of this.shardIds) {
|
|
255
|
-
for (let i = 0; i < this.virtualNodes; i++) {
|
|
256
|
-
const hash = hashString(`${shardId}:${i}`);
|
|
257
|
-
this.consistentHashRing.set(hash, shardId);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
getShardIndex(key) {
|
|
262
|
-
const shardCount = this.shardIds.length;
|
|
263
|
-
if (shardCount === 0) {
|
|
264
|
-
throw new RoutingError("No shards available");
|
|
265
|
-
}
|
|
266
|
-
if (this.strategy === "consistent-hash") {
|
|
267
|
-
return this.getIndexConsistentHash(key);
|
|
268
|
-
}
|
|
269
|
-
return this.getIndexModulo(key, shardCount);
|
|
270
|
-
}
|
|
271
|
-
getIndexModulo(key, shardCount) {
|
|
272
|
-
const hash = hashString(key);
|
|
273
|
-
return hash % shardCount;
|
|
274
|
-
}
|
|
275
|
-
getIndexConsistentHash(key) {
|
|
276
|
-
const hash = hashString(key);
|
|
277
|
-
const sortedHashes = Array.from(this.consistentHashRing.keys()).sort((a, b) => a - b);
|
|
278
|
-
for (const ringHash of sortedHashes) {
|
|
279
|
-
if (hash <= ringHash) {
|
|
280
|
-
const shardId = this.consistentHashRing.get(ringHash);
|
|
281
|
-
const match2 = shardId.match(/shard_(\d+)/);
|
|
282
|
-
return match2 ? parseInt(match2[1], 10) - 1 : 0;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
const firstShardId = this.consistentHashRing.get(sortedHashes[0]);
|
|
286
|
-
const match = firstShardId.match(/shard_(\d+)/);
|
|
287
|
-
return match ? parseInt(match[1], 10) - 1 : 0;
|
|
288
|
-
}
|
|
289
|
-
getShardId(key) {
|
|
290
|
-
const index = this.getShardIndex(key);
|
|
291
|
-
return this.shardIds[index] || `shard_${index + 1}`;
|
|
292
|
-
}
|
|
293
|
-
getRandomShardIndex() {
|
|
294
|
-
return Math.floor(Math.random() * this.shardIds.length);
|
|
295
|
-
}
|
|
296
|
-
getRandomShardId() {
|
|
297
|
-
const index = this.getRandomShardIndex();
|
|
298
|
-
return this.shardIds[index];
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
|
|
302
302
|
// src/core/sharding.ts
|
|
303
303
|
var PrismaSharding = class {
|
|
304
304
|
constructor(config) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-sharding",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Lightweight database sharding library for Prisma with connection pooling and health monitoring",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -13,9 +13,16 @@
|
|
|
13
13
|
"sharding",
|
|
14
14
|
"database",
|
|
15
15
|
"postgresql",
|
|
16
|
+
"db-sharding",
|
|
16
17
|
"horizontal-scaling",
|
|
17
|
-
"connection-pool"
|
|
18
|
+
"connection-pool",
|
|
19
|
+
"cli"
|
|
18
20
|
],
|
|
21
|
+
"bin": {
|
|
22
|
+
"prisma-sharding-migrate": "./dist/cli/migrate.js",
|
|
23
|
+
"prisma-sharding-studio": "./dist/cli/studio.js",
|
|
24
|
+
"prisma-sharding-test": "./dist/cli/test.js"
|
|
25
|
+
},
|
|
19
26
|
"files": [
|
|
20
27
|
"dist"
|
|
21
28
|
],
|
|
@@ -27,7 +34,9 @@
|
|
|
27
34
|
}
|
|
28
35
|
},
|
|
29
36
|
"scripts": {
|
|
30
|
-
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
37
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean && tsup src/cli/migrate.ts src/cli/studio.ts src/cli/test.ts --format cjs --outDir dist/cli --clean",
|
|
38
|
+
"build:lib": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
39
|
+
"build:cli": "tsup src/cli/migrate.ts src/cli/studio.ts src/cli/test.ts --format cjs --outDir dist/cli",
|
|
31
40
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
32
41
|
"lint": "eslint src --ext .ts",
|
|
33
42
|
"typecheck": "tsc --noEmit",
|
|
@@ -40,8 +49,13 @@
|
|
|
40
49
|
"peerDependencies": {
|
|
41
50
|
"@prisma/client": ">=5.0.0"
|
|
42
51
|
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"dotenv": "^16.3.1",
|
|
54
|
+
"pg": "^8.18.0"
|
|
55
|
+
},
|
|
43
56
|
"devDependencies": {
|
|
44
57
|
"@types/node": "^20.10.0",
|
|
58
|
+
"@types/pg": "^8.16.0",
|
|
45
59
|
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
|
46
60
|
"@typescript-eslint/parser": "^6.13.0",
|
|
47
61
|
"eslint": "^8.55.0",
|