Connect by JBRH Open Connect

How a mail cursor loses messages, and how to stop it

A mail agent reads forward through a mailbox and remembers where it got to. If that position advances on any exit path rather than only on a completed one, a refusal marks messages as done that nothing ever looked at — and the provider will not offer them again. The rule here is that a refusal holds work and leaves the read position exactly where it was.

Status
Available What this means
Audience
both, developer
Last verified
Product version
6.3.2

The mechanism, which is duller than the consequence#

Fetch a batch, process it, advance the position past it. The defect is entirely in the third step's placement: written after the batch rather than after the batch *succeeded*, it runs on the path where processing was refused, and the refusal is now invisible.

The daily allowance is what makes this concrete. Metering bounds how much mail is processed for a workspace; when it is spent, more mail arriving is not a reason to process it. If the read position advanced anyway, the messages that arrived after the limit would be permanently unseen — not delayed, not queued, unseen — and no error would exist anywhere to say so.

Exit pathThe read positionThe work
Batch processedAdvances past itDone and recorded
Allowance spentDoes not moveHeld, with the refusal shown in Needs You
Provider error mid-batchDoes not move past what was not processedRetried when the fetch next runs
Nothing to fetchUnchangedNothing to do — the quiet-mailbox case

Held rather than dropped#

The distinction the design turns on is between refusing work and discarding it. A refusal that leaves the position intact is a pause: when the allowance resets, the same messages are still ahead of the cursor and are processed in order. A refusal that advances the position is a deletion with no delete operation and no audit entry.

It is also why the refusal has to surface. The allowance being spent appears in Needs You rather than only in a log, because "Connect stopped reading mail four hours ago" is a fact somebody running the business needs before a customer tells them.

The orderings the rule depends on#

  1. Advance only after the work is durable, never after the fetch.

    Result Re-reading a message that was already handled is cheap. Skipping one is not recoverable, so the safe direction of failure is repetition.

  2. Make repetition safe, because the rule guarantees it will happen.

    Result Anything after a refusal re-reads the same window. Ingest has to end in the same canonical record it would have produced the first time, rather than in a second copy of a conversation.

  3. Keep the provider's shape separate from the canonical one. The tenant_* tables hold what the adapter stored; threads, messages and contacts are what the engine and the screens read, and bridge_* copies between them.

    Result The engine never reads a provider table, so a fault in one adapter cannot corrupt a workspace's conversation history, and re-fetching affects only the provider-shaped side.

  4. Treat the position as owned by the fetch, not by the engine.

    Result Two things advancing one cursor is the other way to lose mail, and it is much harder to see afterwards than a single mistaken advance.

Checking, and what cannot be recovered#

  • Compare windows, not totals. Take a day the workspace was busy and count messages at the provider against threads and messages on the canonical side. A gap that starts at a moment rather than being spread out is a cursor event; a gap spread evenly is usually a filter or a folder.
  • Look for a refusal at the boundary of the gap. A spent allowance is recorded and visible; if the gap begins where the refusal is, the two are the same story and the rule is doing its job.
  • Check for the invisible-row case. Mail arriving into a mailbox row that no list can see looks exactly like mail that was never fetched.
  • Recovery is provider-dependent and is not promised here. Whether an already-skipped window can be re-read depends on what that provider still offers: UNKNOWN in general. The rule exists because prevention is the only reliable half.

Questions#

What happens when the daily allowance runs out?

Processing stops and the read position stays where it is. The mail is still there, the refusal appears in Needs You with its reason, and work resumes in order when the allowance resets or the plan changes. Nothing is discarded and nothing is silently skipped.

Can messages be lost permanently by this?

That is precisely the outcome the rule prevents. A position that advanced on a refusal would leave messages behind it that nothing will look at again, with no error to point at. Advancing only on completed work makes the worst case a repeat rather than a loss.

Does the read position ever move backwards?

It is not treated as a control to wind back. The design assumes forward-only movement gated on success, and repetition at the boundary; anything else is a provider-specific recovery rather than a feature of the sync.