You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
V3 (POST /api/v3/profile) now dedups AAPS profile-store edits via a
profile-shape-aware identifier (uuidv5 of "profilestore_<app>_<defaultProfile>")
and a relaxed immutability rule for date/created_at/startDate during
profile-store deduplication. V1 and V3 paths now both converge AAPS
profile edits onto a single MongoDB doc per source.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: docs/aaps-profile-sync-impact-analysis-2026-04-20.md
+14-11Lines changed: 14 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -294,22 +294,25 @@ A `DataSyncSelectorV1Test` does not currently exist (only V3 has a test). The si
294
294
-**Should `PairProfileStore` track `nightscoutId`?** Every other `Pair*` does. Tracking would let AAPS use stable `_id` references and allow the c-r-m server to do `_id`-based dedup rather than `startDate`-based. It would also unblock a future `nsUpdate` path for profiles.
295
295
-**The `createdAt % 1000 == 0L` heuristic in `NsIncomingDataProcessor.processProfile:283-295`** ("whole second means edited in NS") is fragile — any AAPS-originated profile whose epoch happens to land on a whole second will be re-imported as if NS-edited. Worth replacing with explicit provenance.
296
296
297
-
## V3 path verification
297
+
## V3 path verification and fix
298
298
299
299
V3 hits `POST /api/v3/profile` (`NightscoutRemoteService.kt:102-103`), which routes to `lib/api3/generic/create/operation.js`. The handler computes an identifier as `uuid.v5("undefined_<doc.date>")` (since profile docs have no `device` or `eventType`) and uses it for dedup via `identifyingFilter`. Verified with three new tests in `tests/api3.aaps-patterns.test.js` under `Profile sync via REST (V3) - AAPS createProfileStore behavior`:
| Edit profile in AAPS → new `LocalProfileLastChange` → new `date`|**201, new doc inserted** (different identifier) |
301
+
| Scenario | V3 pre-fix | V3 post-fix |
302
+
|---|---|---|
303
+
| First POST | 201, one doc inserted | 201, one doc inserted |
304
+
| Resend identical payload (retry) | 200, deduped in place (request-level dedup works) | 200, deduped in place |
305
+
| Edit profile in AAPS → new `LocalProfileLastChange` → new `date`|**201, new doc inserted** (different identifier) |**200, same identifier, single doc**|
306
+
| Distinct `defaultProfile` names from same `app`| (n/a) | 201 each, two docs (no collision) |
307
+
308
+
So V3 had **request-level dedup but not edit-level dedup**. We patched it in two places:
309
+
310
+
1.`lib/api3/shared/operationTools.js` — `calculateIdentifier` now special-cases profile-store-shaped documents (`defaultProfile` + `store`, no `eventType`) and computes `uuid.v5("profilestore_<app>_<defaultProfile>")`. Edits and retries from the same `(app, defaultProfile)` collapse onto a single identifier.
311
+
2.`lib/api3/generic/update/validate.js` — relaxed immutability of `date`, `created_at`, `startDate` during deduplication when the storage document is a profile-store, since those fields are expected to advance per edit.
306
312
307
-
So V3 has **request-level dedup but not edit-level dedup**. Each user edit still accumulates a new MongoDB profile document — the same architectural symptom as pre-fix V1.
313
+
After the fix, V3 (REST) and V1 (websocket) both converge AAPS profile updates onto a single MongoDB document per source. The secondary `_id` sort in `lib/server/profile.js:last()` continues to ensure deterministic display when multiple profile rows do exist (e.g., legacy data from before the fix).
308
314
309
-
**Implications:**
310
-
- After the c-r-m fix, V1 is now *better* than V3 for AAPS-shaped profile edits: V1 dedups by `startDate` and converges to a single document; V3 still accumulates one document per edit.
311
-
- Both paths rely on `ctx.profile.last()` to choose the displayed profile, so the secondary sort key fix (`{startDate: -1, _id: -1}`) helps both.
312
-
- A symmetric fix for V3 would extend `calculateIdentifier` to dedup profile docs by `startDate` (or by `(app, startDate)`), or AAPS could be modified to issue `nsUpdate` (PUT/PATCH) instead of `nsAdd` for profile re-saves so the existing identifier is reused.
315
+
**Migration note:** existing duplicate profile rows in production databases are not retroactively merged. New POSTs from updated servers will pick the most recently-edited row (via the `dedupFallbackFields: ['created_at']` path) only if the standard identifier path doesn't match — in practice, after this fix new edits from any given AAPS source converge on one identifier, but pre-existing duplicates remain until manually cleaned up.
0 commit comments