prostgles-server 4.2.639 → 4.2.640

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 (81) hide show
  1. package/debug/sync-logging-build.log +4 -0
  2. package/debug/sync-logging-main.log +688 -0
  3. package/debug/sync-logging-server-build.log +0 -0
  4. package/debug/sync-race-client-build.log +12 -0
  5. package/debug/sync-race-main-e2e-final.log +659 -0
  6. package/debug/sync-race-main-e2e.log +643 -0
  7. package/debug/sync-race-server-build.log +11 -0
  8. package/debug/sync-race-test-lib-final.log +59 -0
  9. package/debug/sync-race-test-lib.log +59 -0
  10. package/debug/sync-review-build.log +4 -0
  11. package/debug/sync-review-client-build.log +0 -0
  12. package/debug/sync-review-main-final.log +685 -0
  13. package/debug/sync-review-main.log +644 -0
  14. package/debug/sync-review-server-build.log +0 -0
  15. package/debug/sync-review-syncData.js +147 -0
  16. package/debug/sync-review-without-reconcile.log +647 -0
  17. package/dist/Auth/AuthHandler.d.ts +2 -2
  18. package/dist/Auth/AuthHandler.d.ts.map +1 -1
  19. package/dist/Auth/AuthHandler.js.map +1 -1
  20. package/dist/Auth/AuthTypes.d.ts +2 -2
  21. package/dist/Auth/AuthTypes.d.ts.map +1 -1
  22. package/dist/Auth/utils/getClientRequestIPsInfo.d.ts +2 -2
  23. package/dist/Auth/utils/getClientRequestIPsInfo.d.ts.map +1 -1
  24. package/dist/DBEventsManager.d.ts +5 -5
  25. package/dist/DBEventsManager.d.ts.map +1 -1
  26. package/dist/DBEventsManager.js.map +1 -1
  27. package/dist/DboBuilder/DboBuilderTypes.d.ts +1 -1
  28. package/dist/DboBuilder/DboBuilderTypes.d.ts.map +1 -1
  29. package/dist/DboBuilder/runSql/QueryStreamer.d.ts +2 -2
  30. package/dist/DboBuilder/runSql/QueryStreamer.d.ts.map +1 -1
  31. package/dist/Logging.d.ts +4 -0
  32. package/dist/Logging.d.ts.map +1 -1
  33. package/dist/Prostgles.d.ts +4 -4
  34. package/dist/Prostgles.d.ts.map +1 -1
  35. package/dist/Prostgles.js.map +1 -1
  36. package/dist/ProstglesTypes.d.ts +3 -3
  37. package/dist/ProstglesTypes.d.ts.map +1 -1
  38. package/dist/PubSubManager/PubSubManager.d.ts +8 -7
  39. package/dist/PubSubManager/PubSubManager.d.ts.map +1 -1
  40. package/dist/PubSubManager/PubSubManager.js.map +1 -1
  41. package/dist/PubSubManager/SyncReplication/getSyncUtilFunctions.d.ts +2 -2
  42. package/dist/PubSubManager/SyncReplication/getSyncUtilFunctions.d.ts.map +1 -1
  43. package/dist/PubSubManager/SyncReplication/getSyncUtilFunctions.js +1 -3
  44. package/dist/PubSubManager/SyncReplication/getSyncUtilFunctions.js.map +1 -1
  45. package/dist/PubSubManager/SyncReplication/logSyncError.d.ts +6 -0
  46. package/dist/PubSubManager/SyncReplication/logSyncError.d.ts.map +1 -0
  47. package/dist/PubSubManager/SyncReplication/logSyncError.js +18 -0
  48. package/dist/PubSubManager/SyncReplication/logSyncError.js.map +1 -0
  49. package/dist/PubSubManager/SyncReplication/syncData.d.ts.map +1 -1
  50. package/dist/PubSubManager/SyncReplication/syncData.js +48 -27
  51. package/dist/PubSubManager/SyncReplication/syncData.js.map +1 -1
  52. package/dist/PubSubManager/addSync.d.ts.map +1 -1
  53. package/dist/PubSubManager/addSync.js +3 -3
  54. package/dist/PubSubManager/addSync.js.map +1 -1
  55. package/dist/PubSubManager/addTrigger.d.ts +2 -2
  56. package/dist/PubSubManager/addTrigger.d.ts.map +1 -1
  57. package/dist/PubSubManager/addTrigger.js.map +1 -1
  58. package/dist/WebsocketAPI/onSocketConnected.d.ts +2 -2
  59. package/dist/WebsocketAPI/onSocketConnected.d.ts.map +1 -1
  60. package/dist/WebsocketAPI/onSocketConnected.js.map +1 -1
  61. package/dist/WebsocketAPI/pushSocketSchema.d.ts +2 -2
  62. package/dist/WebsocketAPI/pushSocketSchema.d.ts.map +1 -1
  63. package/dist/WebsocketAPI/pushSocketSchema.js.map +1 -1
  64. package/lib/Auth/AuthHandler.ts +2 -2
  65. package/lib/Auth/AuthTypes.ts +2 -2
  66. package/lib/Auth/utils/getClientRequestIPsInfo.ts +2 -2
  67. package/lib/DBEventsManager.ts +5 -5
  68. package/lib/DboBuilder/DboBuilderTypes.ts +1 -1
  69. package/lib/DboBuilder/runSql/QueryStreamer.ts +2 -2
  70. package/lib/Logging.ts +5 -0
  71. package/lib/Prostgles.ts +2 -2
  72. package/lib/ProstglesTypes.ts +3 -3
  73. package/lib/PubSubManager/PubSubManager.ts +7 -6
  74. package/lib/PubSubManager/SyncReplication/getSyncUtilFunctions.ts +3 -8
  75. package/lib/PubSubManager/SyncReplication/logSyncError.ts +22 -0
  76. package/lib/PubSubManager/SyncReplication/syncData.ts +54 -30
  77. package/lib/PubSubManager/addSync.ts +3 -4
  78. package/lib/PubSubManager/addTrigger.ts +2 -2
  79. package/lib/WebsocketAPI/onSocketConnected.ts +2 -2
  80. package/lib/WebsocketAPI/pushSocketSchema.ts +2 -2
  81. package/package.json +1 -1
package/lib/Logging.ts CHANGED
@@ -43,6 +43,11 @@ export namespace EventTypes {
43
43
  channelName: string;
44
44
  syncParams: SyncParams;
45
45
  } & (
46
+ | {
47
+ command: "replicationError";
48
+ source: "client" | "trigger" | "WAL";
49
+ error: unknown;
50
+ }
46
51
  | {
47
52
  command: "syncData";
48
53
  source: "client" | "trigger";
package/lib/Prostgles.ts CHANGED
@@ -13,7 +13,7 @@ import type { ProstglesInitOptions } from "./ProstglesTypes";
13
13
  import { RestApi } from "./RestApi";
14
14
  import { TableConfigurator } from "./TableConfig/TableConfigurator";
15
15
 
16
- import type { PRGLIOSocket } from "./DboBuilder/DboBuilder";
16
+ import type { ClientSocketWithCachedData } from "./DboBuilder/DboBuilder";
17
17
  import { DBHandlerServer, DboBuilder } from "./DboBuilder/DboBuilder";
18
18
  export { DBHandlerServer };
19
19
  export type PGP = pgPromise.IMain<{}, pg.IClient>;
@@ -401,7 +401,7 @@ export class Prostgles {
401
401
 
402
402
  init = initProstgles.bind(this);
403
403
 
404
- connectedSockets: PRGLIOSocket[] = [];
404
+ connectedSockets: ClientSocketWithCachedData[] = [];
405
405
  setupSocketIO() {
406
406
  this.checkDb();
407
407
 
@@ -16,7 +16,7 @@ import type { TableConfig } from "./TableConfig/TableConfigTypes";
16
16
  import type { TableHooks } from "./TableHooks/TableHooks";
17
17
  import type { ResolvedAuditConfig, SchemaConfigAudit } from "./Audit/AuditTypes";
18
18
 
19
- import type { PRGLIOSocket } from "./DboBuilder/DboBuilder";
19
+ import type { ClientSocketWithCachedData } from "./DboBuilder/DboBuilder";
20
20
 
21
21
  import type e from "express";
22
22
  import type pgPromise from "pg-promise";
@@ -248,14 +248,14 @@ export type ProstglesInitOptions<
248
248
  * Use for connection verification. Will disconnect socket on any errors
249
249
  */
250
250
  onSocketConnect?: (
251
- args: AuthRequestParams<S, SUser> & { socket: PRGLIOSocket },
251
+ args: AuthRequestParams<S, SUser> & { socket: ClientSocketWithCachedData },
252
252
  ) => void | Promise<void>;
253
253
 
254
254
  /**
255
255
  * Called when a socket disconnects
256
256
  */
257
257
  onSocketDisconnect?: (
258
- args: AuthRequestParams<S, SUser> & { socket: PRGLIOSocket },
258
+ args: AuthRequestParams<S, SUser> & { socket: ClientSocketWithCachedData },
259
259
  ) => void | Promise<void>;
260
260
 
261
261
  /**
@@ -7,7 +7,7 @@ import type {
7
7
  DBHandlerServer,
8
8
  DboBuilder,
9
9
  LocalParams,
10
- PRGLIOSocket,
10
+ ClientSocketWithCachedData,
11
11
  } from "../DboBuilder/DboBuilder";
12
12
  import type { PostgresNotifListenManager } from "../PostgresNotifListenManager";
13
13
  import type { DB } from "../Prostgles";
@@ -60,12 +60,13 @@ export type SyncParams = {
60
60
  throttle?: number;
61
61
  lr?: AnyObject;
62
62
  is_syncing: boolean;
63
+ syncQueue?: Promise<void>;
63
64
  handlers: ReturnType<(typeof ReplicationProtocol)["getServerHandlers"]>;
64
65
  localParams: LocalParams;
65
66
  };
66
67
 
67
68
  export type AddSyncParams = SyncTableInfo & {
68
- socket: PRGLIOSocket;
69
+ socket: ClientSocketWithCachedData;
69
70
  table_info: TableSchema;
70
71
  table_rules: ParsedTableRule;
71
72
  allow_delete?: boolean;
@@ -120,7 +121,7 @@ export type SubscriptionParams = {
120
121
  subscribeOptions: SubscribeOptions;
121
122
 
122
123
  onData?: OnData;
123
- socket: PRGLIOSocket | undefined;
124
+ socket: ClientSocketWithCachedData | undefined;
124
125
  localParams?: LocalParams;
125
126
 
126
127
  lastPushed: number;
@@ -196,7 +197,7 @@ export class PubSubManager {
196
197
  * Updated through refreshTriggers()
197
198
  */
198
199
  _triggers: PubSubManagerTriggers = new Map();
199
- sockets: Map<string, PRGLIOSocket> = new Map();
200
+ sockets: Map<string, ClientSocketWithCachedData> = new Map();
200
201
 
201
202
  subs: Subscription[] = [];
202
203
  syncs: SyncParams[] = [];
@@ -266,7 +267,7 @@ export class PubSubManager {
266
267
 
267
268
  removeSubscription = (
268
269
  channelName: string,
269
- subInfo: { type: "local"; onData: OnData } | { type: "ws"; socket: PRGLIOSocket },
270
+ subInfo: { type: "local"; onData: OnData } | { type: "ws"; socket: ClientSocketWithCachedData },
270
271
  ) => {
271
272
  const matchingSubIdx = this.subs.findIndex(
272
273
  (s) =>
@@ -364,7 +365,7 @@ export class PubSubManager {
364
365
 
365
366
  pushSubData = pushSubData.bind(this);
366
367
 
367
- upsertSocket(socket: PRGLIOSocket | undefined) {
368
+ upsertSocket(socket: ClientSocketWithCachedData | undefined) {
368
369
  if (socket && !this.sockets.get(socket.id)) {
369
370
  this.sockets.set(socket.id, socket);
370
371
  socket.on("disconnect", () => {
@@ -6,7 +6,7 @@ import {
6
6
  type AnyObject,
7
7
  type SyncBatchParams,
8
8
  } from "prostgles-types";
9
- import type { LocalParams, PRGLIOSocket } from "../../DboBuilder/DboBuilder";
9
+ import type { LocalParams, ClientSocketWithCachedData } from "../../DboBuilder/DboBuilder";
10
10
  import type { TableHandler } from "../../DboBuilder/TableHandler/TableHandler";
11
11
  import { getSyncOrderByAndFields } from "./getSyncOrderByAndFields";
12
12
  import type { PubSubManager, SyncParams } from "../PubSubManager";
@@ -21,7 +21,7 @@ import { log } from "../PubSubManagerUtils";
21
21
  import type { EventTypes } from "../../Logging";
22
22
 
23
23
  type Args = {
24
- socket: PRGLIOSocket;
24
+ socket: ClientSocketWithCachedData;
25
25
  tableHandler: TableHandler;
26
26
  sync: SyncParams;
27
27
  pubSubManager: PubSubManager;
@@ -389,12 +389,7 @@ export const getSyncUtilFunctions = ({
389
389
  return;
390
390
  }
391
391
  if (sync.lr?.[synced_field] && +sync.lr[synced_field] > +lastRow[synced_field]) {
392
- console.error(
393
- {
394
- syncIssue: "sync.lr[synced_field] is greater than lastRow[synced_field]",
395
- },
396
- sync.table_name,
397
- );
392
+ return;
398
393
  }
399
394
  sync.lr = lastRow;
400
395
  },
@@ -0,0 +1,22 @@
1
+ import type { EventTypes } from "../../Logging";
2
+ import type { PubSubManager, SyncParams } from "../PubSubManager";
3
+
4
+ export const logSyncError = (
5
+ pubSubManager: PubSubManager,
6
+ sync: SyncParams,
7
+ source: Extract<EventTypes.Sync, { command: "replicationError" }>["source"],
8
+ error: unknown,
9
+ ) =>
10
+ pubSubManager._log({
11
+ type: "sync",
12
+ command: "replicationError",
13
+ source,
14
+ error,
15
+ tableName: sync.table_name,
16
+ channelName: sync.channel_name,
17
+ socketId: sync.socket_id,
18
+ sid: sync.sid,
19
+ connectedSocketIds: pubSubManager.connectedSocketIds,
20
+ syncParams: sync,
21
+ duration: -1,
22
+ });
@@ -4,6 +4,7 @@ import { WAL } from "prostgles-types/dist/WAL";
4
4
  import type { PubSubManager, SyncParams } from "../PubSubManager";
5
5
  import { getSyncUtilFunctions } from "./getSyncUtilFunctions";
6
6
  import type { EventTypes } from "../../Logging";
7
+ import { logSyncError } from "./logSyncError";
7
8
 
8
9
  export type ClientSyncInfo = Partial<{
9
10
  c_fr: AnyObject;
@@ -45,7 +46,26 @@ export type ClientExpressData = ClientSyncInfo & {
45
46
  /**
46
47
  * Server or client requested data sync
47
48
  */
48
- export async function syncData(
49
+ export function syncData(
50
+ this: PubSubManager,
51
+ sync: SyncParams,
52
+ clientData: ClientExpressData | undefined,
53
+ source: "trigger" | "client",
54
+ ) {
55
+ const run = () =>
56
+ runSyncData.call(this, sync, clientData, source).catch(async (error) => {
57
+ if (this.sockets.get(sync.socket_id)?.connected) {
58
+ await logSyncError(this, sync, source, error);
59
+ }
60
+ throw error;
61
+ });
62
+ const queued = (sync.syncQueue ?? Promise.resolve()).then(run);
63
+ // Report failures to the caller while allowing the next request to run.
64
+ sync.syncQueue = queued.catch(() => undefined);
65
+ return queued;
66
+ }
67
+
68
+ async function runSyncData(
49
69
  this: PubSubManager,
50
70
  sync: SyncParams,
51
71
  clientData: ClientExpressData | undefined,
@@ -111,8 +131,12 @@ export async function syncData(
111
131
  sync.is_syncing = true;
112
132
  },
113
133
  onSend: async (data) => {
114
- const res = await upsertData(data, "WAL");
115
- return res;
134
+ try {
135
+ return await upsertData(data, "WAL");
136
+ } catch (error) {
137
+ await logSyncError(this, sync, "WAL", error);
138
+ throw error;
139
+ }
116
140
  },
117
141
  onSendEnd: (batch, _, error) => {
118
142
  if (error === undefined) {
@@ -164,36 +188,36 @@ export async function syncData(
164
188
  }
165
189
 
166
190
  sync.is_syncing = true;
191
+ try {
192
+ let from_synced = null;
193
+
194
+ /** Client runs must reconcile the client's snapshot after reattachment. */
195
+ if (sync.lr && !clientData) {
196
+ const { s_lr } = await getServerRowInfo();
197
+
198
+ /* Make sure trigger is not firing on freshly synced data */
199
+ if (!rowsFullyMatch(sync.lr, s_lr)) {
200
+ from_synced = Number(sync.lr[synced_field]);
201
+ await logSyncData("sync.lr");
202
+ } else {
203
+ await logSyncData("rowsFullyMatch");
204
+ }
205
+ } else {
206
+ await logSyncData("getLastSynced(clientData).start");
207
+ from_synced = await getLastSynced(clientData);
208
+ await logSyncData("getLastSynced(clientData).end");
209
+ }
167
210
 
168
- // from synced does not make sense. It should be sync.lr only!!!
169
- let from_synced = null;
170
-
171
- /** Sync was already synced */
172
- if (sync.lr) {
173
- const { s_lr } = await getServerRowInfo();
174
-
175
- /* Make sure trigger is not firing on freshly synced data */
176
- if (!rowsFullyMatch(sync.lr, s_lr)) {
177
- from_synced = Number(sync.lr[synced_field]);
178
- await logSyncData("sync.lr");
211
+ if (from_synced !== null) {
212
+ await logSyncData("syncBatch.start");
213
+ await syncBatch(from_synced);
214
+ await logSyncData("syncBatch.end");
179
215
  } else {
180
- await logSyncData("rowsFullyMatch");
216
+ await logSyncData("nothingToSync");
181
217
  }
182
- } else {
183
- await logSyncData("getLastSynced(clientData).start");
184
- from_synced = await getLastSynced(clientData);
185
- await logSyncData("getLastSynced(clientData).end");
186
- }
187
218
 
188
- if (from_synced !== null) {
189
- await logSyncData("syncBatch.start");
190
- await syncBatch(from_synced);
191
- await logSyncData("syncBatch.end");
192
- } else {
193
- await logSyncData("nothingToSync");
219
+ await pushData({ state: "synced" });
220
+ } finally {
221
+ sync.is_syncing = false;
194
222
  }
195
-
196
- await pushData({ state: "synced" });
197
-
198
- sync.is_syncing = false;
199
223
  }
@@ -40,7 +40,6 @@ export async function addSync(
40
40
  /* Only a sync per socket per table/condition/select allowed */
41
41
  const existing = find(this.syncs, { socket_id: socket.id, channel_name: channelName });
42
42
  if (existing) {
43
- console.warn("addSync: Client tried to create a duplicate sync", existing.channel_name);
44
43
  return existing;
45
44
  }
46
45
 
@@ -112,18 +111,18 @@ export async function addSync(
112
111
  cb(null, { res: "ok" });
113
112
  });
114
113
 
114
+ this.syncs.push(newSync);
115
115
  return newSync;
116
116
  };
117
117
 
118
- const newSync = upsertSync();
119
-
120
118
  await this.addTrigger(
121
119
  { table_name, condition: conditionParsed, tracked_columns: undefined },
122
120
  undefined,
123
121
  socket,
124
122
  );
125
123
 
126
- this.syncs.push(newSync);
124
+ // Check and register without yielding so concurrent attachments share one sync.
125
+ const newSync = upsertSync();
127
126
  return { channelName, newSync };
128
127
  });
129
128
 
@@ -1,6 +1,6 @@
1
1
  import * as crypto from "crypto";
2
2
  import { asName, pickKeys, tryCatchV2 } from "prostgles-types";
3
- import type { PRGLIOSocket } from "../DboBuilder/DboBuilderTypes";
3
+ import type { ClientSocketWithCachedData } from "../DboBuilder/DboBuilderTypes";
4
4
  import type { TableHandler } from "../DboBuilder/TableHandler/TableHandler";
5
5
  import type { ViewSubscriptionOptions } from "./PubSubManager";
6
6
  import { type PubSubManager } from "./PubSubManager";
@@ -16,7 +16,7 @@ export async function addTrigger(
16
16
  this: PubSubManager,
17
17
  params: AddTriggerParams,
18
18
  viewOptions: ViewSubscriptionOptions | undefined,
19
- socket: PRGLIOSocket | undefined,
19
+ socket: ClientSocketWithCachedData | undefined,
20
20
  ) {
21
21
  const addedTrigger = await tryCatchV2(async () => {
22
22
  const { table_name } = { ...params };
@@ -2,12 +2,12 @@ import type { AnyObject, SocketFunctionCall } from "prostgles-types";
2
2
  import { CHANNELS, getSerialisableError, isObject } from "prostgles-types";
3
3
  import { getClientRequestIPsInfo } from "../Auth/AuthHandler";
4
4
  import type { AuthResultWithSID, SessionUser } from "../Auth/AuthTypes";
5
- import type { PRGLIOSocket } from "../DboBuilder/DboBuilderTypes";
5
+ import type { ClientSocketWithCachedData } from "../DboBuilder/DboBuilderTypes";
6
6
  import type { DBOFullyTyped } from "../DBSchemaBuilder/DBSchemaBuilder";
7
7
  import type { Prostgles, TABLE_METHODS } from "../Prostgles";
8
8
  import { runClientMethod, runClientRequest } from "../runClientRequest";
9
9
 
10
- export async function onSocketConnected(this: Prostgles, socket: PRGLIOSocket) {
10
+ export async function onSocketConnected(this: Prostgles, socket: ClientSocketWithCachedData) {
11
11
  const isDestroyed = () => this.destroyed;
12
12
  if (isDestroyed()) {
13
13
  console.log("Socket connected to destroyed instance");
@@ -1,9 +1,9 @@
1
1
  import { CHANNELS, getSerialisableError, type SQLRequest } from "prostgles-types";
2
- import type { PRGLIOSocket } from "../DboBuilder/DboBuilderTypes";
2
+ import type { ClientSocketWithCachedData } from "../DboBuilder/DboBuilderTypes";
3
3
  import type { Prostgles } from "../Prostgles";
4
4
  import { runClientSqlRequest } from "../runClientRequest";
5
5
  import { makeSocketError } from "./onSocketConnected";
6
- export async function pushSocketSchema(this: Prostgles, socket: PRGLIOSocket) {
6
+ export async function pushSocketSchema(this: Prostgles, socket: ClientSocketWithCachedData) {
7
7
  const isDestroyed = () => this.destroyed;
8
8
  if (isDestroyed()) return;
9
9
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prostgles-server",
3
- "version": "4.2.639",
3
+ "version": "4.2.640",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",