> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Sync Handling

> Best practices for designing and operating subscriptions in Ditto's query-based sync model.

Ditto uses a query-based sync model: each peer declares the data it wants to receive through a subscription query, and the mesh delivers matching documents — incrementally, at the field level, even across intermittent connections. Because subscription queries define what flows through the mesh, how you design and operate them is the primary lever for sync efficiency.

This page covers operational best practices for subscriptions. For the conceptual model, see [Syncing Data](/key-concepts/syncing-data); for the SDK API, see [Managing Subscriptions](/sdk/latest/sync/syncing-data).

<CardGroup>
  <Card title="Syncing Data" icon="rotate" iconType="solid" href="/key-concepts/syncing-data">
    The conceptual model: subscription queries, delta sync, and CRDT-based conflict resolution.
  </Card>

  <Card title="Managing Subscriptions" icon="code" iconType="solid" href="/sdk/latest/sync/syncing-data">
    SDK reference for registering, retrieving, and canceling subscriptions in each language.
  </Card>

  <Card title="Stateful Subscription Performance Guidelines" icon="gauge-high" iconType="solid" href="/stateful-subscriptions">
    How `LIMIT` and `ORDER BY` clauses affect sync performance, and how to write subscription queries that scale.
  </Card>

  <Card title="Mesh Networking" icon="circle-nodes" iconType="solid" href="/key-concepts/mesh-networking">
    How peers discover each other and propagate updates across multiple transports.
  </Card>
</CardGroup>

## Scope subscriptions to the data each peer needs

In a query-based sync model, a peer only receives documents that match the queries it has subscribed to. A well-scoped subscription reduces bandwidth, storage, and CPU on every device in the mesh, and lets the sync engine prioritize the data that matters to the user in front of the device. Subscribe to the narrowest set of documents the device actually needs — filter by tenant, region, role, or whatever dimensions partition your data — rather than syncing entire collections.

When peers run the same subscription, Ditto's flood-fill propagation works at full efficiency: any peer with matching documents can serve any other peer that wants them, regardless of who originally produced the data. Where practical, align subscription shapes across peers in the same role.

## Keep subscriptions stable

Each time a subscription is registered, canceled, or changed, peers across the mesh re-evaluate what they owe the local device and the sync engine reconciles state. Doing this frequently degrades sync throughput and can interrupt in-flight transfers.

As a general guideline, avoid changing subscriptions more often than every fifteen minutes or so. Register subscriptions when the relevant data first becomes needed — at app start, after login, or when the user enters a workspace — and cancel them when that data is no longer relevant. To filter the data a user sees within a stable subscription, run a local query against the result set rather than re-registering the subscription with new arguments.

<Note>
  When subscriptions are created dynamically, cancel those that are no longer needed as new ones are registered. Subscriptions remain active until explicitly canceled, so unreleased subscriptions can accumulate over time and affect sync performance.
</Note>

### Subscription lifecycle in C++ and Rust

In the C++ and Rust SDKs, `register_subscription` returns a `SyncSubscription` wrapped in a `shared_ptr` (C++) or an owned handle (Rust). The caller is the sole owner — Ditto does not retain an internal reference to the subscription object.

<Warning>
  Dropping the last `shared_ptr<SyncSubscription>` (C++) or letting the handle go out of scope (Rust) does **not** cancel the subscription. The underlying query continues replicating until the `Ditto` instance is destroyed. Over time, leaked subscriptions accumulate and can cause elevated CPU and network usage.
</Warning>

Always call `cancel()` explicitly when a subscription is no longer needed:

<CodeGroup>
  ```cpp C++ theme={null}
  // Cancel explicitly before dropping the pointer
  subscription->cancel();
  subscription.reset();
  ```

  ```rust Rust theme={null}
  // Cancel explicitly before the handle is dropped
  subscription.cancel();
  drop(subscription);
  ```
</CodeGroup>

To guarantee automatic cancellation when a subscription leaves scope, wrap it in an RAII guard with a custom deleter:

```cpp C++ theme={null}
// RAII wrapper: cancels the subscription when the shared_ptr is destroyed
auto make_auto_cancel(std::shared_ptr<SyncSubscription> sub) {
  return std::shared_ptr<SyncSubscription>(
    sub.get(),
    [sub](SyncSubscription *s) mutable {
      s->cancel();
      sub.reset();
    });
}

// Usage
{
  auto subscription = make_auto_cancel(
    ditto.sync().register_subscription("SELECT * FROM cars")
  );
  // subscription is automatically canceled at end of scope
}
```

## Avoid stateful subscription patterns

Subscription queries that use `LIMIT`, or `LIMIT` combined with `ORDER BY`, maintain state about which documents currently fall within the limit boundary. When a document inside the boundary changes — is deleted, no longer matches the `WHERE` clause, or moves position under the `ORDER BY` — Ditto must invalidate its cache and re-evaluate the query. This is manageable when the relevant fields rarely change, but expensive when they change often.

<Tip>
  **Key rule:** avoid filtering (`WHERE`) or sorting (`ORDER BY`) on mutable fields when `LIMIT` is used.
</Tip>

For the full guide — query examples, cache-invalidation rules, and migration strategies — see [Stateful Subscription Performance Guidelines](/stateful-subscriptions).

## Keep subscription queries simple

Subscription query complexity affects not just local performance but Big Peer stability. Queries with deeply nested predicates — multiple levels of `AND`, `OR`, or nested object traversals in `WHERE` clauses — require more server-side processing to evaluate. In extreme cases, overly complex queries can degrade the subscription server's ability to serve all connected peers.

Guidelines for subscription query complexity:

* **Flatten predicates where possible.** Instead of deeply nesting conditions, restructure your data model or break queries into separate subscriptions with simpler filters.
* **Watch for programmatically generated queries.** Code that dynamically builds `WHERE` clauses by iterating over arrays or nested structures can produce queries with far more nesting than expected. Review the actual query strings in your debug logs.
* **Prefer simple field comparisons.** Conditions like `field == value` or `field > value` are efficient. Deeply nested object paths and compound conditions add processing overhead.

<Tip>
  If your peers begin logging `503 Service Unavailable` errors when connecting to the Big Peer, overly complex subscription queries are a likely cause. See the [Troubleshooting Guide](/sdk/latest/deployment/troubleshooting#subscription-server-overload-503-errors) for diagnostic steps.
</Tip>

## Use multiple transports

Ditto's multiplexer chooses among Wi-Fi, Bluetooth Low Energy, and WebSocket connections automatically based on availability and conditions. Leave all relevant transports enabled in your transport configuration so the runtime can switch as devices move between environments. See [Customizing Transport Configurations](/sdk/latest/sync/customizing-transport-configurations).

## Rely on built-in conflict resolution

Concurrent edits across peers resolve deterministically through Ditto's CRDT merge rules — applications do not need locking, write-time conflict callbacks, or custom merge code. Designing data models that work with these rules (rather than against them) is the main contributor to predictable behavior under concurrent writes. See [Conflict Resolution Patterns](/best-practices/conflict-resolution-patterns).

## Design for offline-first operation

Ditto stores subscribed data locally and continues to read, write, and resolve conflicts while disconnected. When connectivity returns, peers reconcile their changes through delta sync — only the modified fields transit the network. Build flows that assume the local database is always available and treat connectivity as opportunistic.
