Internal
Public Access
Backfill data missed by devices with a pre-tag-sync token
/api/sync/ only returns rows changed since a device's last sync token. Any device that synced before tag/time-entry syncing existed already has a token from that era; pre-existing tags untouched since then were never "changed since" that token and so were permanently invisible to that device, even after tag syncing shipped. Adds a sync_format_version check that ignores the stored token and forces exactly one full resync per device when it detects an old-format token, backfilling anything that was missed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
3fbab2b831
commit
373bfe6a6e
@@ -7,6 +7,15 @@
|
|||||||
|
|
||||||
const BACKGROUND_SYNC_MIN_INTERVAL_MS = 2 * 60 * 1000;
|
const BACKGROUND_SYNC_MIN_INTERVAL_MS = 2 * 60 * 1000;
|
||||||
|
|
||||||
|
// Bump this whenever a previously-untracked entity type starts being synced
|
||||||
|
// (e.g. tags/time_entries were added after tasks-only syncing already
|
||||||
|
// shipped). /api/sync/ only returns rows changed since a device's last sync
|
||||||
|
// token, so any device with an old token would otherwise never receive
|
||||||
|
// pre-existing rows of the newly-tracked type - they were never "changed
|
||||||
|
// since" a token issued before that type existed at all. Forces exactly one
|
||||||
|
// full resync (by clearing the stored token) per bump, per device.
|
||||||
|
const SYNC_FORMAT_VERSION = 2;
|
||||||
|
|
||||||
function getCsrfToken() {
|
function getCsrfToken() {
|
||||||
const el = document.querySelector('[name=csrfmiddlewaretoken]');
|
const el = document.querySelector('[name=csrfmiddlewaretoken]');
|
||||||
return el ? el.value : null;
|
return el ? el.value : null;
|
||||||
@@ -103,7 +112,9 @@ async function runBackgroundSync() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const deviceId = await ensureDeviceId();
|
const deviceId = await ensureDeviceId();
|
||||||
const lastSyncToken = await getMeta('last_sync_token');
|
const syncFormatVersion = await getMeta('sync_format_version');
|
||||||
|
const forceFullResync = (syncFormatVersion || 1) < SYNC_FORMAT_VERSION;
|
||||||
|
const lastSyncToken = forceFullResync ? null : await getMeta('last_sync_token');
|
||||||
|
|
||||||
const dirtyTasks = await getDirtyTasks();
|
const dirtyTasks = await getDirtyTasks();
|
||||||
const dirtyTags = await getDirtyTags();
|
const dirtyTags = await getDirtyTags();
|
||||||
@@ -154,8 +165,12 @@ async function runBackgroundSync() {
|
|||||||
|
|
||||||
await setMeta('last_sync_token', data.sync_token);
|
await setMeta('last_sync_token', data.sync_token);
|
||||||
await setMeta('last_sync_at', new Date().toISOString());
|
await setMeta('last_sync_at', new Date().toISOString());
|
||||||
|
await setMeta('sync_format_version', SYNC_FORMAT_VERSION);
|
||||||
|
|
||||||
return hadPendingChanges;
|
// A forced full resync just backfilled previously-missed data (e.g. tags
|
||||||
|
// that existed before tag syncing shipped) - worth a refresh even if
|
||||||
|
// nothing local was dirty, since the current page may be missing it too.
|
||||||
|
return hadPendingChanges || forceFullResync;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function maybeBackgroundSync() {
|
async function maybeBackgroundSync() {
|
||||||
|
|||||||
+1
-1
@@ -148,7 +148,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<script src="{% static 'js/offline-db.js' %}?v=7"></script>
|
<script src="{% static 'js/offline-db.js' %}?v=7"></script>
|
||||||
<script src="{% static 'js/offline-sync.js' %}?v=8"></script>
|
<script src="{% static 'js/offline-sync.js' %}?v=9"></script>
|
||||||
<script src="{% static 'js/app.js' %}?v=8"></script>
|
<script src="{% static 'js/app.js' %}?v=8"></script>
|
||||||
{% block extra_js %}{% endblock %}
|
{% block extra_js %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user