Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion reference/components/scheduler.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export async function syncExchangeRates(context) {
}
```

**Handlers should be idempotent.** Harper's clustering has no distributed lock, so leadership failover and daylight-saving fall-back can occasionally deliver the same logical occurrence twice. Design handlers so that running twice for one occurrence is harmless.
**Handlers should be idempotent.** Scheduler delivery does not acquire a distributed lock for each occurrence, so leadership failover and daylight-saving fall-back can occasionally deliver the same logical occurrence twice. Design handlers so that running twice for one occurrence is harmless. Harper's [native record locks](../database/concurrency.md#native-record-locks) can protect participating record updates within their supported scope; they do not make scheduler delivery or external side effects exactly once.

**A failed run is not retried.** If a handler throws (or its outbound request times out), the run is recorded with `lastStatus: error` and the occurrence is not made up - the job simply fires again at its next scheduled time. Handlers that need delivery guarantees should implement their own bounded retry/backoff inside the handler.

Expand Down
1 change: 1 addition & 0 deletions reference/configuration/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ replication:
- `hostname` <VersionBadge type="changed" version="v5.3.0" /> — This instance's hostname within the cluster. Subject to the same bare hostname or IP literal requirement as [`node.hostname`](#node) — a URL, `host:port`, or non-string value fails startup. When both are set, `node.hostname` wins.
- `url` — WebSocket URL peers use to connect to this instance
- `databases` — Databases to replicate; _Default_: `"*"` (all). Each entry supports `name` and `sharded`
- `recordLocks` <VersionBadge version="v5.3.0" /> - Boolean enabling cluster record locks in Harper Pro on RocksDB; _Default_: `false`. Set the YAML boolean `true`, not a quoted string, and restart for the change to take effect. An agreed home map is also required; the flag alone does not establish cluster protection. See [native record locks](../database/concurrency.md#native-record-locks) for setup and limits.
Comment thread
hdbjeff marked this conversation as resolved.
Resolved
- `routes` — Peer nodes; URL strings or `{hostname, port, startTime, revokedCertificates}` objects
- `port` — Replication port
- `securePort` — Secure replication port; _Default_: `9933` (changed from `9925` in v4.5.0)
Expand Down
6 changes: 4 additions & 2 deletions reference/database/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,18 +153,20 @@ On normal callback completion the transaction is committed automatically. If the

### Transaction Scope and Atomicity

Transactions span a single database. All tables within the same database share a single transactional context: reads return a consistent snapshot, and writes across multiple tables are committed atomically. If code accesses tables in different databases, each database gets its own transaction with no cross-database atomicity guarantee.
Transactions span a single database. All tables within the same database share a single transactional context: reads normally return a consistent snapshot, and writes across multiple tables are committed atomically. Lock acquisition can refresh the read state, as described below. If code accesses tables in different databases, each database gets its own transaction with no cross-database atomicity guarantee.

For deeper background on Harper's transaction model, see [Storage Algorithm](./storage-algorithm.md).

## Read Consistency

Each request reads from a consistent point-in-time snapshot. Within a single request:
Ordinary reads in a request use a consistent point-in-time snapshot. Within a single request:

- a query response never mixes partially-written records;
- a long or streamed scan holds the snapshot it started with, so it does not observe writes that land mid-scan;
- multiple reads in the same request transaction see the same snapshot.

Native `Table.lock()` is an exception: it reloads committed data and can release an earlier read snapshot. Acquire the lock before reading the value used in a condition; do not rely on a pre-lock read remaining repeatable after acquisition. See [Concurrency and Consistency](./concurrency.md#native-record-locks) for lock scope and limits.

Across separate requests there is no shared snapshot. A common pattern — querying for matching rows in one request, then fetching each by id in a later request — runs the two reads at two different points in time. If the data changes in between (a row is deleted or expires, say), the second read can disagree with the first; a query may return an id whose subsequent fetch returns a 404. This is expected: it is two independent snapshots, not a consistency bug.

For repeatable-read consistency across a query and a fetch, perform both reads within a single resource method or `transaction()` callback.
Expand Down
Loading
Loading