prostgles-server 4.2.638 → 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 (94) 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/TableConfig/getCreateSchemaQueries.d.ts.map +1 -1
  59. package/dist/TableConfig/getCreateSchemaQueries.js +3 -2
  60. package/dist/TableConfig/getCreateSchemaQueries.js.map +1 -1
  61. package/dist/TableConfig/getTableConfigVersion.d.ts +2 -0
  62. package/dist/TableConfig/getTableConfigVersion.d.ts.map +1 -0
  63. package/dist/TableConfig/getTableConfigVersion.js +11 -0
  64. package/dist/TableConfig/getTableConfigVersion.js.map +1 -0
  65. package/dist/TableConfig/runMigrations.d.ts.map +1 -1
  66. package/dist/TableConfig/runMigrations.js +12 -6
  67. package/dist/TableConfig/runMigrations.js.map +1 -1
  68. package/dist/WebsocketAPI/onSocketConnected.d.ts +2 -2
  69. package/dist/WebsocketAPI/onSocketConnected.d.ts.map +1 -1
  70. package/dist/WebsocketAPI/onSocketConnected.js.map +1 -1
  71. package/dist/WebsocketAPI/pushSocketSchema.d.ts +2 -2
  72. package/dist/WebsocketAPI/pushSocketSchema.d.ts.map +1 -1
  73. package/dist/WebsocketAPI/pushSocketSchema.js.map +1 -1
  74. package/lib/Auth/AuthHandler.ts +2 -2
  75. package/lib/Auth/AuthTypes.ts +2 -2
  76. package/lib/Auth/utils/getClientRequestIPsInfo.ts +2 -2
  77. package/lib/DBEventsManager.ts +5 -5
  78. package/lib/DboBuilder/DboBuilderTypes.ts +1 -1
  79. package/lib/DboBuilder/runSql/QueryStreamer.ts +2 -2
  80. package/lib/Logging.ts +5 -0
  81. package/lib/Prostgles.ts +2 -2
  82. package/lib/ProstglesTypes.ts +3 -3
  83. package/lib/PubSubManager/PubSubManager.ts +7 -6
  84. package/lib/PubSubManager/SyncReplication/getSyncUtilFunctions.ts +3 -8
  85. package/lib/PubSubManager/SyncReplication/logSyncError.ts +22 -0
  86. package/lib/PubSubManager/SyncReplication/syncData.ts +54 -30
  87. package/lib/PubSubManager/addSync.ts +3 -4
  88. package/lib/PubSubManager/addTrigger.ts +2 -2
  89. package/lib/TableConfig/getCreateSchemaQueries.ts +4 -3
  90. package/lib/TableConfig/getTableConfigVersion.ts +6 -0
  91. package/lib/TableConfig/runMigrations.ts +19 -9
  92. package/lib/WebsocketAPI/onSocketConnected.ts +2 -2
  93. package/lib/WebsocketAPI/pushSocketSchema.ts +2 -2
  94. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import type { AnyObject } from "prostgles-types";
2
2
  import { CHANNELS, getProperty, isDefined } from "prostgles-types";
3
- import type { PRGLIOSocket } from "../DboBuilder/DboBuilder";
3
+ import type { ClientSocketWithCachedData } from "../DboBuilder/DboBuilder";
4
4
  import type { DBOFullyTyped } from "../DBSchemaBuilder/DBSchemaBuilder";
5
5
  import type { Prostgles } from "../Prostgles";
6
6
  import type { AuthClientRequest, AuthConfig, BasicSession } from "./AuthTypes";
@@ -226,7 +226,7 @@ export class AuthHandler {
226
226
  getSidAndUserFromRequest = getSidAndUserFromRequest.bind(this);
227
227
 
228
228
  isNonExpiredSocketSession = (
229
- socket: PRGLIOSocket,
229
+ socket: ClientSocketWithCachedData,
230
230
  session: BasicSession | undefined,
231
231
  ): boolean => {
232
232
  const hasExpired = Boolean(session && session.expires <= Date.now());
@@ -24,7 +24,7 @@ import type {
24
24
  UserLike,
25
25
  } from "prostgles-types";
26
26
  import type { DBOFullyTyped } from "../DBSchemaBuilder/DBSchemaBuilder";
27
- import type { PRGLIOSocket } from "../DboBuilder/DboBuilderTypes";
27
+ import type { ClientSocketWithCachedData } from "../DboBuilder/DboBuilderTypes";
28
28
  import { type CachedSession } from "../DboBuilder/DboBuilderTypes";
29
29
  import type { DB } from "../Prostgles";
30
30
  import { GET_AUTH_ROUTE } from "./AuthHandler";
@@ -52,7 +52,7 @@ export type BasicSession = {
52
52
  onExpiration: "redirect" | "show_error";
53
53
  };
54
54
 
55
- type SocketClientRequest = { socket: PRGLIOSocket; httpReq?: undefined };
55
+ type SocketClientRequest = { socket: ClientSocketWithCachedData; httpReq?: undefined };
56
56
  type HttpClientRequest = {
57
57
  httpReq: ExpressReq;
58
58
  res: ExpressRes;
@@ -1,7 +1,7 @@
1
- import type { PRGLIOSocket } from "../../DboBuilder/DboBuilderTypes";
1
+ import type { ClientSocketWithCachedData } from "../../DboBuilder/DboBuilderTypes";
2
2
  import type { ExpressReq, LoginClientInfo } from "../AuthTypes";
3
3
  type ClientReq =
4
- | { socket: PRGLIOSocket; httpReq?: undefined }
4
+ | { socket: ClientSocketWithCachedData; httpReq?: undefined }
5
5
  | { httpReq: ExpressReq; socket?: undefined };
6
6
  export const getClientRequestIPsInfo = <T extends ClientReq>(req: T): LoginClientInfo => {
7
7
  if (req.httpReq) {
@@ -2,7 +2,7 @@ import type { PrglNotifListener } from "./PostgresNotifListenManager";
2
2
  import { PostgresNotifListenManager } from "./PostgresNotifListenManager";
3
3
  import type { DB, PGP } from "./Prostgles";
4
4
  import { getKeys, CHANNELS } from "prostgles-types";
5
- import type { PRGLIOSocket } from "./DboBuilder/DboBuilder";
5
+ import type { ClientSocketWithCachedData } from "./DboBuilder/DboBuilder";
6
6
 
7
7
  export class DBEventsManager {
8
8
  notifies: {
@@ -65,7 +65,7 @@ export class DBEventsManager {
65
65
 
66
66
  async addNotify(
67
67
  query: string,
68
- socket?: PRGLIOSocket,
68
+ socket?: ClientSocketWithCachedData,
69
69
  func?: any
70
70
  ): Promise<{
71
71
  socketChannel: string;
@@ -142,7 +142,7 @@ export class DBEventsManager {
142
142
  };
143
143
  }
144
144
 
145
- removeNotify(channel?: string, socket?: PRGLIOSocket, func?: any) {
145
+ removeNotify(channel?: string, socket?: ClientSocketWithCachedData, func?: any) {
146
146
  const notifChannel = channel && this.notifies[channel];
147
147
  if (notifChannel) {
148
148
  if (socket) {
@@ -163,7 +163,7 @@ export class DBEventsManager {
163
163
  }
164
164
  }
165
165
 
166
- addNotice(socket: PRGLIOSocket) {
166
+ addNotice(socket: ClientSocketWithCachedData) {
167
167
  if (!socket.id) throw "Expecting a socket obj with id";
168
168
 
169
169
  if (!this.notice.sockets.find((s) => s.id === socket.id)) {
@@ -180,7 +180,7 @@ export class DBEventsManager {
180
180
  return { socketChannel, socketUnsubChannel };
181
181
  }
182
182
 
183
- removeNotice(socket: PRGLIOSocket) {
183
+ removeNotice(socket: ClientSocketWithCachedData) {
184
184
  if (!socket.id) throw "Expecting a socket obj with id";
185
185
  this.notice.sockets = this.notice.sockets.filter((s) => s.id !== socket.id);
186
186
  }
@@ -146,7 +146,7 @@ export type CachedSession = {
146
146
  __prglCache?: Map<string, CachedSessionData>;
147
147
  };
148
148
 
149
- export type PRGLIOSocket = Socket & {
149
+ export type ClientSocketWithCachedData = Socket & {
150
150
  readonly id: string;
151
151
 
152
152
  readonly handshake: {
@@ -6,14 +6,14 @@ import type { BasicCallback } from "../../PubSubManager/PubSubManager";
6
6
  import type { VoidFunction } from "../../SchemaWatch/SchemaWatch";
7
7
  import type { DB } from "../../initProstgles";
8
8
  import type { DboBuilder } from "../DboBuilder";
9
- import type { PRGLIOSocket } from "../DboBuilderTypes";
9
+ import type { ClientSocketWithCachedData } from "../DboBuilderTypes";
10
10
  import { getErrorAsObject, getSerializedClientErrorFromPGError } from "../dboBuilderUtils";
11
11
  import { getDetailedFieldInfo } from "./runSqlUtils";
12
12
  import { getConnectionDetails } from "./getAdminClient";
13
13
  const Cursor = require("pg-cursor") as typeof CursorType;
14
14
 
15
15
  type ClientStreamedRequest = {
16
- socket: PRGLIOSocket;
16
+ socket: ClientSocketWithCachedData;
17
17
  query: string;
18
18
  options: SQLOptions | undefined;
19
19
  persistConnection?: boolean;
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 };
@@ -4,6 +4,7 @@ import type { DB } from "../initProstgles";
4
4
  import type { ProstglesInitOptions, TableConfigMigrations } from "../ProstglesTypes";
5
5
  import { fetchTableConstraints } from "./fetchTableConstraints";
6
6
  import type { TableConfig } from "./TableConfigTypes";
7
+ import { getTableConfigVersion } from "./getTableConfigVersion";
7
8
  export type RequiredUndefined<T> = {
8
9
  [K in keyof Required<T>]: T[K];
9
10
  };
@@ -62,10 +63,10 @@ const getMigration = async (
62
63
  { version, onMigrate }: Pick<TableConfigMigrations, "version" | "onMigrate">,
63
64
  file_table_queries: string,
64
65
  ) => {
65
- const maxVersion = Number(
66
- (await db.oneOrNone<{ v: string }>(`SELECT MAX(id) as v FROM ${asName(versionTableName)}`))?.v,
66
+ const latestVersion = getTableConfigVersion(
67
+ (await db.oneOrNone<{ v: string | null }>(`SELECT MAX(id) as v FROM ${asName(versionTableName)}`))
68
+ ?.v,
67
69
  );
68
- const latestVersion = Number.isFinite(maxVersion) ? maxVersion : undefined;
69
70
 
70
71
  if (latestVersion === version) {
71
72
  const isLatest = (
@@ -0,0 +1,6 @@
1
+ export const getTableConfigVersion = (value: string | null | undefined) => {
2
+ if (value === null || value === undefined) return;
3
+
4
+ const version = Number(value);
5
+ return Number.isFinite(version) ? version : undefined;
6
+ };
@@ -2,6 +2,7 @@ import type pgPromise from "pg-promise";
2
2
  import type { ProstglesInitOptions } from "../ProstglesTypes";
3
3
  import { EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID } from "../PubSubManager/PubSubManagerUtils";
4
4
  import { fetchTableConstraints } from "./fetchTableConstraints";
5
+ import { getTableConfigVersion } from "./getTableConfigVersion";
5
6
 
6
7
  export async function runMigrations(
7
8
  t: pgPromise.ITask<{}>,
@@ -9,26 +10,35 @@ export async function runMigrations(
9
10
  tableConfigMigrations,
10
11
  tableConfig,
11
12
  }: Pick<ProstglesInitOptions, "tableConfigMigrations" | "tableConfig">,
12
- { asName }: { asName: (name: string) => string }
13
+ { asName }: { asName: (name: string) => string },
13
14
  ) {
14
15
  if (!tableConfigMigrations) return;
15
16
 
16
17
  const { onMigrate, version, versionTableName = "schema_version" } = tableConfigMigrations;
17
18
  await t.any(`
18
- /* ${EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID} */
19
- CREATE TABLE IF NOT EXISTS ${asName(versionTableName)}(id NUMERIC PRIMARY KEY, table_config JSONB NOT NULL)
20
- `);
19
+ /* ${EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID} */
20
+ CREATE TABLE IF NOT EXISTS ${asName(versionTableName)}(
21
+ id NUMERIC PRIMARY KEY,
22
+ table_config JSONB NOT NULL
23
+ )
24
+ `);
21
25
  const migrations = { version, table: versionTableName };
22
- const maxVersion = Number(
23
- (await t.oneOrNone<{ v: string }>(`SELECT MAX(id) as v FROM ${asName(versionTableName)}`))?.v
26
+ const latestVersion = getTableConfigVersion(
27
+ (
28
+ await t.oneOrNone<{ v: string | null }>(
29
+ `SELECT MAX(id) as v FROM ${asName(versionTableName)}`,
30
+ )
31
+ )?.v,
24
32
  );
25
- const latestVersion = Number.isFinite(maxVersion) ? maxVersion : undefined;
26
33
 
27
34
  if (latestVersion === version) {
28
35
  const isLatest = (
29
36
  await t.oneOrNone<{ v: string | null }>(
30
- `SELECT table_config = \${table_config} as v FROM ${asName(versionTableName)} WHERE id = \${version}`,
31
- { version, table_config: tableConfig }
37
+ `
38
+ SELECT table_config = \${tableConfig} as v
39
+ FROM ${asName(versionTableName)}
40
+ WHERE id = \${version}`,
41
+ { version, tableConfig },
32
42
  )
33
43
  )?.v;
34
44
  if (isLatest) {
@@ -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.638",
3
+ "version": "4.2.640",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",