Pluggable store for Plaid transaction sync cursors.
Cursors represent the position in Plaid's transaction event log. They MUST be persisted durably — if a cursor is lost, the next sync call will restart from the beginning (full historical replay).
Production persistence note
By default, cursors are persisted via PlaidEx.Sync.CursorStore.EtsBackend,
which means cursors are LOST on application restart. For production
systems, implement Behaviour with a database backend (Ecto, Redix,
etc.) and configure it:
config :plaid_ex,
cursor_store: MyApp.PlaidCursorStoreCustom cursor store
defmodule MyApp.PlaidCursorStore do
@behaviour PlaidEx.Sync.CursorStore.Behaviour
@impl true
def get(item_id) do
case Repo.get_by(PlaidItem, item_id: item_id) do
nil -> nil
item -> item.sync_cursor
end
end
@impl true
def put(item_id, cursor) do
Repo.update_all(
from(i in PlaidItem, where: i.item_id == ^item_id),
set: [sync_cursor: cursor]
)
:ok
end
@impl true
def delete(item_id) do
Repo.delete_all(from(i in PlaidItem, where: i.item_id == ^item_id))
:ok
end
end
Summary
Functions
Deletes the cursor for an item, forcing a full re-sync on next run.
Retrieves the cursor for the given item ID.
Returns nil if no cursor exists (fresh sync).
Persists a cursor for the given item ID.
Functions
@spec delete(String.t()) :: :ok
Deletes the cursor for an item, forcing a full re-sync on next run.
Use this when Plaid returns
TRANSACTIONS_SYNC_MUTATION_DURING_PAGINATION — the cursor is
invalidated and a fresh sync must begin from the start.
Retrieves the cursor for the given item ID.
Returns nil if no cursor exists (fresh sync).
Persists a cursor for the given item ID.
IMPORTANT: Call this BEFORE processing the page data. This ensures that on crash, the sync restarts from the correct cursor position (processing the same page again) rather than losing the cursor and starting over.