Skip to content

Commit

Permalink
Use the newest lastSyncAt instead of the eldest one during sync (#…
Browse files Browse the repository at this point in the history
…328)

For #327

Approach

Instead of sending to the server the eldest `lastSyncAt` at Collector's end and updating the `lastSyncAt` of all the entries at the end of the `sync`, it now just sends the newest `lastSyncAt` which falls more natural (no need of modifying the `lastSyncAt` of any other entries at the end). In layman's terms it's asking: "Ok server, can you please bring me all the records that have been created or updated since the last time I synced with you?", where the last time is determined by the last entry that has been fetched (last entry has the most recent `lastSyncAt` value because they come sorted by `updated_at` in `ASCENDING` order  from the server). This allows the syncing to be interrupted and resumed without any problem as it doesn't depend on any event on completion such as updating `lastSyncAt` for all entries at the end of the `sync` process.

Avoiding pitfalls on upload

What happens if other entries or updates are submitted to the server by other collectors while the current one is uploading their own changes? Will the current collector miss those changes? 

No, that's not a problem. In order to cover that scenario, `lastSyncAt` of each entry that is being `uploaded` on `remoteUpload` or `remoteUploadUpdate` is set to the newest `lastSyncAt` in the database at that moment. This allows capturing the new entries or updates that could have been uploaded to the server by other collectors while the current one was uploading their own changes.
  • Loading branch information
lmatayoshi authored Dec 25, 2020
1 parent 3508b1e commit 04ce1ee
Showing 1 changed file with 46 additions and 33 deletions.
79 changes: 46 additions & 33 deletions app/actions/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,18 @@ export const auth = () => async (dispatch, getState) => {
export const remoteSync = (url, user, entityName, mapper) => async () => {
const initializedDb = await db.initializeForUser(user);
const entity = initializedDb[entityName];
const oldestEntity = await entity.findOne({
const newestEntity = await entity.findOne({
where: initializedDb.sequelize.literal(
"lastSyncAt IS NOT 'Invalid date' AND lastSyncAt is NOT NULL"
),
order: [['lastSyncAt', 'ASC']]
order: [['lastSyncAt', 'DESC']]
});
const newestRemoteIdEntity = await entity.findOne({
order: [['remoteId', 'DESC']]
});
const oldestDate =
oldestEntity && oldestEntity.lastSyncAt
? moment(oldestEntity.lastSyncAt).toISOString()
const newestLastSyncAt =
newestEntity && newestEntity.lastSyncAt
? moment(newestEntity.lastSyncAt).toISOString()
: null;
const newestRemoteId = newestRemoteIdEntity
? newestRemoteIdEntity.remoteId
Expand All @@ -125,7 +125,7 @@ export const remoteSync = (url, user, entityName, mapper) => async () => {
user.auth,
{
qs: {
updated_at_gth: oldestDate,
updated_at_gth: newestLastSyncAt,
id_gth: newestRemoteId
}
},
Expand Down Expand Up @@ -180,26 +180,7 @@ export const remoteSync = (url, user, entityName, mapper) => async () => {
)
.catch(e => console.log(e));
}
).then(({ greather_updated_at: greatherUpdatedAt }) => {
if (!greatherUpdatedAt) return;
entity.update(
{
lastSyncAt: moment(greatherUpdatedAt)
.local()
.toDate(),
updatedAt: moment(greatherUpdatedAt)
.local()
.toDate()
},
{
where: initializedDb.sequelize.literal(
"remoteId is NOT NULL AND strftime('%Y-%m-%d %H:%M:%S', updatedAt) <= strftime('%Y-%m-%d %H:%M:%S', lastSyncAt)"
),
silent: true
}
);
return Promise.resolve();
});
);
};

export const remoteUpload = (
Expand All @@ -212,6 +193,19 @@ export const remoteUpload = (
const initializedDb = await db.initializeForUser(user);
const { sequelize } = initializedDb;
const entity = initializedDb[entityName];

const newestEntity = await entity.findOne({
where: initializedDb.sequelize.literal(
"lastSyncAt IS NOT 'Invalid date' AND lastSyncAt is NOT NULL"
),
order: [['lastSyncAt', 'DESC']]
});

const newestLastSyncAt =
newestEntity && newestEntity.lastSyncAt
? moment(newestEntity.lastSyncAt).toISOString()
: null;

const query = withSoftDelete
? "(deletedAt is NULL OR deletedAt IS 'Invalid date') AND (remoteId is NULL OR remoteId = '')"
: "remoteId is NULL OR remoteId = ''";
Expand All @@ -235,9 +229,12 @@ export const remoteUpload = (
});
if (existingEntity && existingEntity.id !== currentEntity.id)
existingEntity.destroy();
// TODO: `updatedAt` is not being updated for some reason
// thus triggering an extra innocuous `update`
return currentEntity.update({
remoteId: res.id,
lastSyncAt: new Date()
lastSyncAt: newestLastSyncAt || new Date(),
updatedAt: newestLastSyncAt || new Date()
});
})
.catch(e => console.log(e));
Expand All @@ -255,6 +252,18 @@ export const remoteUploadUpdate = (
const initializedDb = await db.initializeForUser(user);
const { sequelize } = initializedDb;
const entity = initializedDb[entityName];
const newestEntity = await entity.findOne({
where: initializedDb.sequelize.literal(
"lastSyncAt IS NOT 'Invalid date' AND lastSyncAt is NOT NULL"
),
order: [['lastSyncAt', 'DESC']]
});

const newestLastSyncAt =
newestEntity && newestEntity.lastSyncAt
? moment(newestEntity.lastSyncAt).toISOString()
: null;

const query = withSoftDelete
? "(deletedAt is NULL OR deletedAt IS 'Invalid date') AND (remoteId is NOT NULL AND strftime('%Y-%m-%d %H:%M:%S', updatedAt) > strftime('%Y-%m-%d %H:%M:%S', lastSyncAt))"
: "remoteId is NOT NULL AND strftime('%Y-%m-%d %H:%M:%S', updatedAt) > strftime('%Y-%m-%d %H:%M:%S', lastSyncAt)";
Expand All @@ -271,12 +280,16 @@ export const remoteUploadUpdate = (
.then(item =>
entity.update(
{
lastSyncAt: moment(item.updated_at)
.local()
.toDate(),
updatedAt: moment(item.updated_at)
.local()
.toDate()
lastSyncAt:
newestLastSyncAt ||
moment(item.updated_at)
.local()
.toDate(),
updatedAt:
newestLastSyncAt ||
moment(item.updated_at)
.local()
.toDate()
},
{
where: {
Expand Down

0 comments on commit 04ce1ee

Please sign in to comment.