# `Concord.Sync`
[🔗](https://github.com/gsmlg-dev/concord/blob/main/lib/concord/sync.ex#L1)

Public API for Concord's sync and watch protocol.

## Pull model — `changes/3`

    events = Concord.Sync.changes(1840, 1850)

## Push model — `watch/2`

    {:ok, ref} = Concord.Sync.watch({:prefix, "/tasks/"}, self())

    receive do
      {:concord_event, ^ref, %Event{}} -> ...
    end

    :ok = Concord.Sync.unwatch(ref)

## Stream wrapper — `watch_stream/2`

    Concord.Sync.watch_stream({:prefix, "/tasks/"})
    |> Stream.each(&IO.inspect/1)
    |> Stream.run()

# `changes`

```elixir
@spec changes(non_neg_integer(), non_neg_integer(), keyword()) :: [
  Concord.Sync.Event.t()
]
```

Returns events in the revision range `[from, to]` (inclusive).

## Options

- `:limit` — max events to return (default: 1000)

# `compact`

```elixir
@spec compact(non_neg_integer()) :: non_neg_integer()
```

Compacts the change log, removing entries before `keep_revision`.

# `earliest_revision`

```elixir
@spec earliest_revision() :: non_neg_integer()
```

Returns the earliest revision still in the change log.

# `unwatch`

```elixir
@spec unwatch(reference()) :: :ok
```

Unsubscribes a watch by its reference.

# `watch`

```elixir
@spec watch(Concord.KV.Selector.t(), pid(), keyword()) ::
  {:ok, reference()} | {:error, term()}
```

Subscribes the given process to events matching the selector.

Returns `{:ok, watch_ref}`.

## Options

- `:max_queue` — max pending events before backpressure (default: 1000)

# `watch_stream`

```elixir
@spec watch_stream(
  Concord.KV.Selector.t(),
  keyword()
) :: Enumerable.t()
```

Returns a `Stream` that yields events matching the selector.

The stream blocks on `receive` and yields one event at a time.
Terminates when the calling process receives `:concord_watch_done`.

# `watcher_count`

```elixir
@spec watcher_count() :: non_neg_integer()
```

Returns the number of active watchers.

