Guides
Sync and Backfill
Understand how scheduled syncs, sync jobs, and backfills fit together.
Sync workflow
The main sync workflow runs as a Convex action. At a high level it:
- validates or refreshes tokens
- fetches provider data with pagination
- writes events, time-series points, and summaries in batches
- records sync job state and errors
Cron-based sync
Set up a Convex cron to sync active connections periodically:
// convex/crons.ts
import { cronJobs } from "convex/server";
import { components } from "./_generated/api";
const crons = cronJobs();
crons.interval(
"sync all wearables",
{ minutes: 15 },
components.wearables.syncWorkflow.syncAllActive,
{
clientCredentials: {
strava: {
clientId: process.env.STRAVA_CLIENT_ID!,
clientSecret: process.env.STRAVA_CLIENT_SECRET!,
},
},
syncWindowHours: 24,
},
);
export default crons;Sync jobs
The component tracks sync activity in syncJobs, including:
- status
- timestamps
- current phase
- cursor or pagination progress
- records processed
- workflow identifiers
Use the client API to query recent jobs for a user:
await wearables.getSyncJobs(ctx, { userId: "user-123", limit: 20 });Backfill jobs
Longer historical imports are tracked separately in backfillJobs.
This is especially relevant for Garmin, where push webhooks and regular syncs cover ongoing updates, but a one-time or durable backfill may be needed to hydrate older history.
Interaction with storage policies
Time-series storage policies apply to backfills too.
That means:
- historical points can land directly in rollup tiers
- old data can be deleted if it falls outside the final tier
- maintenance can compact raw or finer rollup data later if policy changes
For cost-sensitive deployments, review Storage Policies before enabling large backfills.