@plusscommunities/pluss-core-aws 2.0.25-beta.1 → 2.0.25-beta.3
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/helper/hqPublishing.js +31 -6
- package/package.json +1 -1
package/helper/hqPublishing.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
const AWS = require("aws-sdk");
|
|
21
21
|
const moment = require("moment");
|
|
22
|
-
const
|
|
22
|
+
const indexQueryRecursive = require("../db/common/indexQueryRecursive");
|
|
23
23
|
const updateRef = require("../db/common/updateRef");
|
|
24
24
|
const editRef = require("../db/common/editRef");
|
|
25
25
|
|
|
@@ -94,12 +94,18 @@ const findCopiesByHqSourceId = async (
|
|
|
94
94
|
if (!hqSourceId) {
|
|
95
95
|
return [];
|
|
96
96
|
}
|
|
97
|
-
|
|
97
|
+
// indexQueryRecursive paginates LastEvaluatedKey internally and returns a
|
|
98
|
+
// flat array — required because a single GSI query response caps at 1 MiB
|
|
99
|
+
// and large-content HQ fan-outs to ~50 client sites can exceed that. With
|
|
100
|
+
// the non-recursive indexQuery, propagateHqEdit + cascadeHqRetract would
|
|
101
|
+
// silently miss copies on page 2+, leaving them stale after edits and
|
|
102
|
+
// visible after retractions.
|
|
103
|
+
const items = await indexQueryRecursive(tableName, {
|
|
98
104
|
IndexName: indexName,
|
|
99
105
|
KeyConditionExpression: "HqSourceId = :sid",
|
|
100
106
|
ExpressionAttributeValues: { ":sid": hqSourceId },
|
|
101
107
|
});
|
|
102
|
-
return
|
|
108
|
+
return items || [];
|
|
103
109
|
};
|
|
104
110
|
|
|
105
111
|
/**
|
|
@@ -136,7 +142,14 @@ const fanOutHqSource = async (hqSourceRow, tableName) => {
|
|
|
136
142
|
return [];
|
|
137
143
|
}
|
|
138
144
|
const copyId = `hqcopy-${hqSourceRow.Id}`;
|
|
139
|
-
|
|
145
|
+
// IMPORTANT: milliseconds (.valueOf), not seconds (.unix). Newsletter
|
|
146
|
+
// rows written by addNewsletterEntry.js use `moment.utc().valueOf()` for
|
|
147
|
+
// UnixTimestamp. UnixTimestampReverse-keyed feed queries (the standard
|
|
148
|
+
// Pluss list pattern) would otherwise rank HQ copies ~1000× ahead of
|
|
149
|
+
// locally-authored posts forever — MAX_SAFE_INTEGER minus a number 10^9
|
|
150
|
+
// is vastly larger than MAX_SAFE_INTEGER minus 10^12. Keep parity with
|
|
151
|
+
// the canonical addNewsletterEntry convention.
|
|
152
|
+
const now = moment.utc().valueOf();
|
|
140
153
|
|
|
141
154
|
const writes = targets.map((targetSiteId) => {
|
|
142
155
|
// Spread + override pattern: keep all content fields, swap identity
|
|
@@ -215,9 +228,17 @@ const propagateHqEdit = async (
|
|
|
215
228
|
}
|
|
216
229
|
propagated.Changed = moment.utc().unix();
|
|
217
230
|
|
|
231
|
+
// IMPORTANT: pass a fresh shallow copy per call. `editRef` synchronously
|
|
232
|
+
// mutates its `updates` argument (`updates[keyCol] = id` inside the Promise
|
|
233
|
+
// constructor) AFTER taking a `cloneDeep` snapshot. Across parallel
|
|
234
|
+
// `Promise.all(map(...))` invocations, iteration N's `cloneDeep` would
|
|
235
|
+
// capture iteration N-1's `RowId` mutation, then later overwrite copy N's
|
|
236
|
+
// `RowId` during the get-merge-put, writing copy N's content back to copy
|
|
237
|
+
// N-1's key (and leaving copy N's own row untouched). Per-call `{...propagated}`
|
|
238
|
+
// gives each invocation an isolated payload it can mutate safely.
|
|
218
239
|
await Promise.all(
|
|
219
240
|
copies.map((copy) =>
|
|
220
|
-
editRef(tableName, "RowId", copy.RowId, propagated),
|
|
241
|
+
editRef(tableName, "RowId", copy.RowId, { ...propagated }),
|
|
221
242
|
),
|
|
222
243
|
);
|
|
223
244
|
return copies.length;
|
|
@@ -256,9 +277,13 @@ const cascadeHqRetract = async (hqSourceRow, tableName, options = {}) => {
|
|
|
256
277
|
Deleted: true,
|
|
257
278
|
Changed: moment.utc().unix(),
|
|
258
279
|
};
|
|
280
|
+
// Per-call shallow copy — see propagateHqEdit for the editRef-mutation-race
|
|
281
|
+
// rationale. Even though `deletionUpdate` is tiny, the same RowId clobber
|
|
282
|
+
// happens here: under sharing, only the FIRST copy actually receives
|
|
283
|
+
// `Deleted: true`; subsequent writes land at the wrong RowId.
|
|
259
284
|
await Promise.all(
|
|
260
285
|
copies.map((copy) =>
|
|
261
|
-
editRef(tableName, "RowId", copy.RowId, deletionUpdate),
|
|
286
|
+
editRef(tableName, "RowId", copy.RowId, { ...deletionUpdate }),
|
|
262
287
|
),
|
|
263
288
|
);
|
|
264
289
|
return copies.length;
|
package/package.json
CHANGED