@dbcube/core 3.0.20 → 3.0.21

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/dist/index.cjs CHANGED
@@ -766,11 +766,166 @@ var import_path2 = __toESM(require("path"));
766
766
  var import_module2 = require("module");
767
767
  var net = __toESM(require("net"));
768
768
  var import_child_process2 = require("child_process");
769
+ var MAX_CONNECTIONS_PER_DB = 10;
770
+ var MAX_CACHE_ENTRIES = 1e3;
771
+ var CACHE_CLEANUP_THRESHOLD = 0.9;
772
+ var CONNECTION_IDLE_TIMEOUT = 3e5;
773
+ var CONNECTION_MAX_AGE = 36e5;
774
+ var LRUCache = class {
775
+ cache;
776
+ maxSize;
777
+ cleanupThreshold;
778
+ constructor(maxSize, cleanupThreshold = 0.9) {
779
+ this.cache = /* @__PURE__ */ new Map();
780
+ this.maxSize = maxSize;
781
+ this.cleanupThreshold = cleanupThreshold;
782
+ }
783
+ get(key) {
784
+ const entry = this.cache.get(key);
785
+ if (!entry) return void 0;
786
+ entry.lastAccessed = Date.now();
787
+ entry.accessCount++;
788
+ return entry.value;
789
+ }
790
+ set(key, value) {
791
+ if (this.cache.size >= this.maxSize * this.cleanupThreshold) {
792
+ this.evictLRU();
793
+ }
794
+ this.cache.set(key, {
795
+ value,
796
+ lastAccessed: Date.now(),
797
+ accessCount: 1
798
+ });
799
+ }
800
+ evictLRU() {
801
+ const entries = Array.from(this.cache.entries()).sort((a, b) => {
802
+ const scoreDiff = this.calculateScore(a[1]) - this.calculateScore(b[1]);
803
+ if (scoreDiff !== 0) return scoreDiff;
804
+ return a[1].lastAccessed - b[1].lastAccessed;
805
+ });
806
+ const toRemove = Math.floor(this.maxSize * 0.2);
807
+ for (let i = 0; i < toRemove && i < entries.length; i++) {
808
+ this.cache.delete(entries[i][0]);
809
+ }
810
+ }
811
+ calculateScore(entry) {
812
+ const now = Date.now();
813
+ const age = now - entry.lastAccessed;
814
+ const recencyScore = 1 / (age + 1);
815
+ const frequencyScore = entry.accessCount;
816
+ return recencyScore * 0.3 + frequencyScore * 0.7;
817
+ }
818
+ clear() {
819
+ this.cache.clear();
820
+ }
821
+ size() {
822
+ return this.cache.size;
823
+ }
824
+ };
825
+ var ConnectionPool = class {
826
+ pools;
827
+ maxConnectionsPerDb;
828
+ constructor(maxConnectionsPerDb = MAX_CONNECTIONS_PER_DB) {
829
+ this.pools = /* @__PURE__ */ new Map();
830
+ this.maxConnectionsPerDb = maxConnectionsPerDb;
831
+ setInterval(() => this.cleanupIdleConnections(), 6e4);
832
+ }
833
+ getConnection(connectionId) {
834
+ const pool = this.pools.get(connectionId);
835
+ if (!pool || pool.length === 0) return null;
836
+ for (let i = 0; i < pool.length; i++) {
837
+ const conn = pool[i];
838
+ if (this.isConnectionValid(conn)) {
839
+ conn.lastUsed = Date.now();
840
+ conn.requestCount++;
841
+ return conn.socket;
842
+ } else {
843
+ conn.socket.destroy();
844
+ pool.splice(i, 1);
845
+ i--;
846
+ }
847
+ }
848
+ return null;
849
+ }
850
+ addConnection(connectionId, socket) {
851
+ if (!this.pools.has(connectionId)) {
852
+ this.pools.set(connectionId, []);
853
+ }
854
+ const pool = this.pools.get(connectionId);
855
+ if (pool.length >= this.maxConnectionsPerDb) {
856
+ const oldest = pool.shift();
857
+ if (oldest) {
858
+ oldest.socket.destroy();
859
+ }
860
+ }
861
+ pool.push({
862
+ socket,
863
+ lastUsed: Date.now(),
864
+ createdAt: Date.now(),
865
+ requestCount: 0
866
+ });
867
+ }
868
+ removeConnection(connectionId, socket) {
869
+ const pool = this.pools.get(connectionId);
870
+ if (!pool) return;
871
+ const index = pool.findIndex((c) => c.socket === socket);
872
+ if (index !== -1) {
873
+ pool[index].socket.destroy();
874
+ pool.splice(index, 1);
875
+ }
876
+ if (pool.length === 0) {
877
+ this.pools.delete(connectionId);
878
+ }
879
+ }
880
+ closeAll(connectionId) {
881
+ if (connectionId) {
882
+ const pool = this.pools.get(connectionId);
883
+ if (pool) {
884
+ pool.forEach((conn) => conn.socket.destroy());
885
+ this.pools.delete(connectionId);
886
+ }
887
+ } else {
888
+ this.pools.forEach((pool) => {
889
+ pool.forEach((conn) => conn.socket.destroy());
890
+ });
891
+ this.pools.clear();
892
+ }
893
+ }
894
+ isConnectionValid(conn) {
895
+ const now = Date.now();
896
+ const age = now - conn.createdAt;
897
+ return conn.socket.readyState === "open" && age < CONNECTION_MAX_AGE;
898
+ }
899
+ cleanupIdleConnections() {
900
+ const now = Date.now();
901
+ this.pools.forEach((pool, connectionId) => {
902
+ for (let i = pool.length - 1; i >= 0; i--) {
903
+ const conn = pool[i];
904
+ const idleTime = now - conn.lastUsed;
905
+ if (idleTime > CONNECTION_IDLE_TIMEOUT || !this.isConnectionValid(conn)) {
906
+ conn.socket.destroy();
907
+ pool.splice(i, 1);
908
+ }
909
+ }
910
+ if (pool.length === 0) {
911
+ this.pools.delete(connectionId);
912
+ }
913
+ });
914
+ }
915
+ getStats() {
916
+ const stats = {};
917
+ this.pools.forEach((pool, id) => {
918
+ stats[id] = {
919
+ activeConnections: pool.length,
920
+ totalRequests: pool.reduce((sum, c) => sum + c.requestCount, 0)
921
+ };
922
+ });
923
+ return stats;
924
+ }
925
+ };
769
926
  var globalTcpServers = /* @__PURE__ */ new Map();
770
- var globalTcpConnections = /* @__PURE__ */ new Map();
771
- var queryCache = /* @__PURE__ */ new Map();
772
- var cacheSize = 0;
773
- var MAX_CACHE_SIZE = 500;
927
+ var connectionPool = new ConnectionPool(MAX_CONNECTIONS_PER_DB);
928
+ var queryCache = new LRUCache(MAX_CACHE_ENTRIES, CACHE_CLEANUP_THRESHOLD);
774
929
  var QueryEngine = class {
775
930
  name;
776
931
  config;
@@ -925,14 +1080,12 @@ var QueryEngine = class {
925
1080
  return new Promise((resolve5) => setTimeout(resolve5, ms));
926
1081
  }
927
1082
  getCachedDML(dmlJson) {
928
- if (queryCache.has(dmlJson)) {
929
- return queryCache.get(dmlJson);
1083
+ const cached = queryCache.get(dmlJson);
1084
+ if (cached !== void 0) {
1085
+ return cached;
930
1086
  }
931
1087
  const parsed = JSON.parse(dmlJson);
932
- if (cacheSize < MAX_CACHE_SIZE) {
933
- queryCache.set(dmlJson, parsed);
934
- cacheSize++;
935
- }
1088
+ queryCache.set(dmlJson, parsed);
936
1089
  return parsed;
937
1090
  }
938
1091
  async startTcpServer() {
@@ -1046,13 +1199,12 @@ var QueryEngine = class {
1046
1199
  });
1047
1200
  }
1048
1201
  async sendTcpRequestFast(args) {
1049
- const existingConnection = globalTcpConnections.get(this.connectionId);
1202
+ const existingConnection = connectionPool.getConnection(this.connectionId);
1050
1203
  if (existingConnection && existingConnection.readyState === "open") {
1051
1204
  try {
1052
1205
  return await this.sendOnExistingConnection(existingConnection, args);
1053
1206
  } catch (error) {
1054
- globalTcpConnections.delete(this.connectionId);
1055
- existingConnection.destroy();
1207
+ connectionPool.removeConnection(this.connectionId, existingConnection);
1056
1208
  }
1057
1209
  }
1058
1210
  return await this.createPersistentConnection(args);
@@ -1137,7 +1289,7 @@ var QueryEngine = class {
1137
1289
  }
1138
1290
  return;
1139
1291
  }
1140
- globalTcpConnections.set(this.connectionId, client);
1292
+ connectionPool.addConnection(this.connectionId, client);
1141
1293
  const command = {
1142
1294
  action: "execute",
1143
1295
  dml: dmlJson
@@ -1164,12 +1316,12 @@ var QueryEngine = class {
1164
1316
  client.on("error", (error) => {
1165
1317
  if (!isResolved) {
1166
1318
  isResolved = true;
1167
- globalTcpConnections.delete(this.connectionId);
1319
+ connectionPool.removeConnection(this.connectionId, client);
1168
1320
  reject(error);
1169
1321
  }
1170
1322
  });
1171
1323
  client.on("close", () => {
1172
- globalTcpConnections.delete(this.connectionId);
1324
+ connectionPool.removeConnection(this.connectionId, client);
1173
1325
  if (!isResolved && !responseBuffer.includes("PROCESS_RESPONSE:")) {
1174
1326
  reject(new Error("Connection closed without valid response"));
1175
1327
  }
@@ -1264,12 +1416,7 @@ var QueryEngine = class {
1264
1416
  });
1265
1417
  }
1266
1418
  async disconnect() {
1267
- const connection = globalTcpConnections.get(this.connectionId);
1268
- if (connection && connection.readyState === "open") {
1269
- connection.write(JSON.stringify({ action: "disconnect" }));
1270
- connection.destroy();
1271
- globalTcpConnections.delete(this.connectionId);
1272
- }
1419
+ connectionPool.closeAll(this.connectionId);
1273
1420
  const serverInfo = globalTcpServers.get(this.connectionId);
1274
1421
  if (serverInfo && serverInfo.process && !serverInfo.process.killed) {
1275
1422
  serverInfo.process.kill();
@@ -1286,6 +1433,21 @@ var QueryEngine = class {
1286
1433
  data: null
1287
1434
  };
1288
1435
  }
1436
+ // Método para obtener estadísticas del pool y cache
1437
+ static getStats() {
1438
+ return {
1439
+ connectionPool: connectionPool.getStats(),
1440
+ cache: {
1441
+ size: queryCache.size(),
1442
+ maxSize: MAX_CACHE_ENTRIES
1443
+ }
1444
+ };
1445
+ }
1446
+ // Método para limpiar todo el cache y conexiones
1447
+ static clearAll() {
1448
+ queryCache.clear();
1449
+ connectionPool.closeAll();
1450
+ }
1289
1451
  };
1290
1452
 
1291
1453
  // src/lib/SqliteExecutor.ts