Ferricstore.Bitcask.NIF (ferricstore v0.10.0)

Copy Markdown View Source

Rustler NIF bindings for Bitcask record I/O, hint files, and mmap-backed probabilistic data structure file operations.

Summary

Functions

Append one binary and fdatasync it without following a final symlink.

Append and fdatasync only when the opened regular file stays within max_bytes.

Durably replace a file atomically when the payload is within max_bytes.

Stream-copy a locked regular file and atomically replace the destination.

Stream-copy a regular file into a new destination without following final symlinks.

Does the path exist? Follows symlinks (broken symlink → false). Never returns an error for the common kinds (missing, no-permission).

Atomically replace the destination with a hard link to a locked regular file.

Is the path a directory? Follows symlinks. Returns false for missing paths (matches File.dir?/1).

List the entries in a directory (names only, no path prefix). Yields every 256 entries to keep BEAM reductions accurate on huge directories.

Recursive mkdir -p. Idempotent when the directory already exists. Equivalent to File.mkdir_p/1.

Read at most max_bytes from a regular file while refusing a symlink at the final path component. The size is checked before the BEAM binary is allocated.

Read a private regular file without following a final symlink.

Atomic rename. On POSIX, rename replaces the target atomically. Cross-device renames return {:error, {:other, _}} — caller must handle with copy+remove.

Remove a single file. Use fs_rm_rf_async/3 for directory trees.

Async recursive remove. Runs on the Tokio blocking pool; sends {:tokio_complete, correlation_id, :ok} or {:tokio_complete, correlation_id, :error, {kind, msg}} to the caller on completion. Idempotent: removing a non-existent path sends :ok.

Creates an empty file if it does not exist. Idempotent on an existing file (does not truncate). Equivalent to File.touch!/1 without the timestamp update (matches our callers' usage).

Fsyncs a directory so that recent File.rename/2, File.rm/1, File.touch!/1, or File.create/1 operations on files inside it are durable. Use after any namespace mutation that must survive a kernel panic — rotation, compaction rename, hint-file creation, prob-file create/delete, shard init.

Strictly scans at most max_records physical log records for tombstones.

Types

append_op()

@type append_op() ::
  {:put, binary(), binary(), non_neg_integer()} | {:delete, binary()}

append_op_location()

@type append_op_location() ::
  {:put, non_neg_integer(), non_neg_integer()}
  | {:delete, non_neg_integer(), non_neg_integer()}

flow_index_claim_entry()

@type flow_index_claim_entry() ::
  {binary(), binary(), float(), binary(), float(), binary(), float(), binary(),
   float(), binary(), binary(), float()}

flow_index_resource()

@type flow_index_resource() :: reference()

flow_record_claim_history_entry()

@type flow_record_claim_history_entry() ::
  {binary(), binary(), non_neg_integer(), non_neg_integer(), binary(), binary(),
   non_neg_integer() | nil, non_neg_integer() | nil, boolean()}

flow_record_claim_history_plan()

@type flow_record_claim_history_plan() ::
  {binary(), flow_index_claim_entry(), binary(), non_neg_integer() | nil,
   flow_record_claim_history_entry()}

flow_record_claim_plan()

@type flow_record_claim_plan() ::
  {binary(), flow_index_claim_entry(), binary(), non_neg_integer() | nil}

fs_error()

@type fs_error() :: {:error, {atom(), binary()}}

lmdb_op()

@type lmdb_op() ::
  {:put, binary(), binary()}
  | {:put_new, binary(), binary()}
  | {:delete, binary()}
  | {:compare, binary(), binary()}
  | {:compare_missing, binary()}

lmdb_original()

@type lmdb_original() :: {binary(), :missing | {:value, binary()}}

pread_batch_result()

@type pread_batch_result() :: [pread_batch_value()]

pread_batch_value()

@type pread_batch_value() :: binary() | nil | {:error, binary()}

Functions

bloom_file_add(path, element)

@spec bloom_file_add(binary(), binary()) :: :ok | {:error, term()}

bloom_file_add_at(path, receipt_path, element, mutation_index, mutation_ordinal)

@spec bloom_file_add_at(
  binary(),
  binary(),
  binary(),
  non_neg_integer(),
  non_neg_integer()
) :: {:ok, 0 | 1} | {:error, term()}

bloom_file_card(path)

@spec bloom_file_card(binary()) :: {:ok, non_neg_integer()} | {:error, term()}

bloom_file_card_async(caller_pid, correlation_id, path)

@spec bloom_file_card_async(pid(), term(), binary()) :: :ok | {:error, term()}

bloom_file_create(path, num_bits, num_hashes)

@spec bloom_file_create(binary(), non_neg_integer(), non_neg_integer()) ::
  :ok | {:error, term()}

bloom_file_exists(path, element)

@spec bloom_file_exists(binary(), binary()) :: {:ok, boolean()} | {:error, term()}

bloom_file_exists_async(caller_pid, correlation_id, path, element)

@spec bloom_file_exists_async(pid(), term(), binary(), binary()) ::
  :ok | {:error, term()}

bloom_file_info(path)

@spec bloom_file_info(binary()) ::
  {:ok, {non_neg_integer(), non_neg_integer(), non_neg_integer()}}
  | {:error, term()}

bloom_file_info_async(caller_pid, correlation_id, path)

@spec bloom_file_info_async(pid(), term(), binary()) :: :ok | {:error, term()}

bloom_file_madd(path, elements)

@spec bloom_file_madd(binary(), [binary()]) :: :ok | {:error, term()}

bloom_file_madd_at(path, receipt_path, elements, mutation_index, mutation_ordinal)

@spec bloom_file_madd_at(
  binary(),
  binary(),
  [binary()],
  non_neg_integer(),
  non_neg_integer()
) :: {:ok, [0 | 1]} | {:error, term()}

bloom_file_mexists(path, elements)

@spec bloom_file_mexists(binary(), [binary()]) ::
  {:ok, [boolean()]} | {:error, term()}

bloom_file_mexists_async(caller_pid, correlation_id, path, elements)

@spec bloom_file_mexists_async(pid(), term(), binary(), [binary()]) ::
  :ok | {:error, term()}

cms_file_create(path, width, depth)

@spec cms_file_create(binary(), non_neg_integer(), non_neg_integer()) ::
  :ok | {:error, term()}

cms_file_incrby(path, items)

@spec cms_file_incrby(binary(), [{binary(), non_neg_integer()}]) ::
  :ok | {:error, term()}

cms_file_incrby_at(path, receipt_path, items, mutation_index, mutation_ordinal)

@spec cms_file_incrby_at(
  binary(),
  binary(),
  [{binary(), integer()}],
  non_neg_integer(),
  non_neg_integer()
) :: {:ok, [integer()]} | {:error, term()}

cms_file_info(path)

@spec cms_file_info(binary()) ::
  {:ok, {non_neg_integer(), non_neg_integer(), non_neg_integer()}}
  | {:error, term()}

cms_file_info_async(caller_pid, correlation_id, path)

@spec cms_file_info_async(pid(), term(), binary()) :: :ok | {:error, term()}

cms_file_merge(dst_path, src_paths, weights)

@spec cms_file_merge(binary(), [binary()], [number()]) :: :ok | {:error, term()}

cms_file_merge_at(dst_path, receipt_path, src_paths, weights, mutation_index, mutation_ordinal)

@spec cms_file_merge_at(
  binary(),
  binary(),
  [binary()],
  [integer()],
  non_neg_integer(),
  non_neg_integer()
) :: :ok | {:error, term()}

cms_file_query(path, elements)

@spec cms_file_query(binary(), [binary()]) ::
  {:ok, [non_neg_integer()]} | {:error, term()}

cms_file_query_async(caller_pid, correlation_id, path, elements)

@spec cms_file_query_async(pid(), term(), binary(), [binary()]) ::
  :ok | {:error, term()}

cuckoo_file_add(path, element)

@spec cuckoo_file_add(binary(), binary()) :: :ok | {:error, term()}

cuckoo_file_add_at(path, receipt_path, element, mutation_index, mutation_ordinal)

@spec cuckoo_file_add_at(
  binary(),
  binary(),
  binary(),
  non_neg_integer(),
  non_neg_integer()
) ::
  {:ok, 0 | 1} | {:error, term()}

cuckoo_file_addnx(path, element)

@spec cuckoo_file_addnx(binary(), binary()) :: {:ok, boolean()} | {:error, term()}

cuckoo_file_addnx_at(path, receipt_path, element, mutation_index, mutation_ordinal)

@spec cuckoo_file_addnx_at(
  binary(),
  binary(),
  binary(),
  non_neg_integer(),
  non_neg_integer()
) ::
  {:ok, 0 | 1} | {:error, term()}

cuckoo_file_count(path, element)

@spec cuckoo_file_count(binary(), binary()) ::
  {:ok, non_neg_integer()} | {:error, term()}

cuckoo_file_count_async(caller_pid, correlation_id, path, element)

@spec cuckoo_file_count_async(pid(), term(), binary(), binary()) ::
  :ok | {:error, term()}

cuckoo_file_create(path, capacity, bucket_size)

@spec cuckoo_file_create(binary(), non_neg_integer(), non_neg_integer()) ::
  :ok | {:error, term()}

cuckoo_file_del(path, element)

@spec cuckoo_file_del(binary(), binary()) :: :ok | {:error, term()}

cuckoo_file_del_at(path, receipt_path, element, mutation_index, mutation_ordinal)

@spec cuckoo_file_del_at(
  binary(),
  binary(),
  binary(),
  non_neg_integer(),
  non_neg_integer()
) ::
  {:ok, 0 | 1} | {:error, term()}

cuckoo_file_exists(path, element)

@spec cuckoo_file_exists(binary(), binary()) :: {:ok, boolean()} | {:error, term()}

cuckoo_file_exists_async(caller_pid, correlation_id, path, element)

@spec cuckoo_file_exists_async(pid(), term(), binary(), binary()) ::
  :ok | {:error, term()}

cuckoo_file_info(path)

@spec cuckoo_file_info(binary()) :: {:ok, tuple()} | {:error, term()}

cuckoo_file_info_async(caller_pid, correlation_id, path)

@spec cuckoo_file_info_async(pid(), term(), binary()) :: :ok | {:error, term()}

cuckoo_file_mexists(path, elements)

@spec cuckoo_file_mexists(binary(), [binary()]) ::
  {:ok, [boolean()]} | {:error, term()}

cuckoo_file_mexists_async(caller_pid, correlation_id, path, elements)

@spec cuckoo_file_mexists_async(pid(), term(), binary(), [binary()]) ::
  :ok | {:error, term()}

flow_history_decode(value)

@spec flow_history_decode(binary()) :: {:ok, list()} | :error

flow_history_encode(event, version, now_ms, id, type, state, priority, attempts, fencing_token, created_at_ms, updated_at_ms, next_run_at_ms, lease_deadline_ms, lease_owner, payload_ref, parent_flow_id, root_flow_id, correlation_id, result_ref, error_ref, rewound_to_event_id, meta_encoded)

@spec flow_history_encode(
  binary(),
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary()
) :: binary()

flow_index_apply_batch(resource, put_entries, put_new_entries, move_entries, delete_entries, claim_entries)

@spec flow_index_apply_batch(
  flow_index_resource(),
  [{binary(), binary(), float()}],
  [{binary(), binary(), float()}],
  [{binary(), binary(), binary(), float()}],
  [{binary(), binary()}],
  [flow_index_claim_entry()]
) :: :ok

flow_index_apply_claim_entries(resource, entries)

@spec flow_index_apply_claim_entries(flow_index_resource(), [flow_index_claim_entry()]) ::
  :ok

flow_index_claim_due_candidates(resource, keys, max_score, limit, max_scan)

@spec flow_index_claim_due_candidates(
  flow_index_resource(),
  [binary()],
  float(),
  non_neg_integer(),
  non_neg_integer()
) :: [{binary(), [{binary(), float()}]}]

flow_index_count_all(resource, key)

@spec flow_index_count_all(flow_index_resource(), binary()) :: non_neg_integer()

flow_index_count_keys_page(resource, cursor, limit)

@spec flow_index_count_keys_page(
  flow_index_resource(),
  binary() | nil,
  non_neg_integer()
) :: [binary()]

flow_index_count_many(resource, keys)

@spec flow_index_count_many(flow_index_resource(), [binary()]) :: [non_neg_integer()]

flow_index_delete_count(resource, key)

@spec flow_index_delete_count(flow_index_resource(), binary()) :: :ok

flow_index_delete_entries(resource, entries)

@spec flow_index_delete_entries(flow_index_resource(), [{binary(), binary()}]) :: :ok

flow_index_delete_members(resource, key, members)

@spec flow_index_delete_members(flow_index_resource(), binary(), [binary()]) :: :ok

flow_index_due_count_keys_page(resource, cursor, limit)

@spec flow_index_due_count_keys_page(
  flow_index_resource(),
  binary() | nil,
  non_neg_integer()
) :: [binary()]

flow_index_due_keys_present(resource, keys, max_score)

@spec flow_index_due_keys_present(flow_index_resource(), [binary()], float()) :: [
  binary()
]

flow_index_earliest_due_score(resource, prefixes, needles, suffixes)

@spec flow_index_earliest_due_score(flow_index_resource(), [binary()], [binary()], [
  binary()
]) ::
  float() | nil | {:error, binary()}

flow_index_fifo_lane_heads(resource, due_key, lane_keys)

@spec flow_index_fifo_lane_heads(flow_index_resource(), binary(), [binary()]) :: [
  {binary(), binary(), float() | nil}
]

flow_index_fifo_lane_heads_many(resource, due_lane_keys)

@spec flow_index_fifo_lane_heads_many(flow_index_resource(), [{binary(), binary()}]) ::
  [
    {binary(), binary(), binary(), float() | nil}
  ]

flow_index_move_entries(resource, entries)

@spec flow_index_move_entries(flow_index_resource(), [
  {binary(), binary(), binary(), float()}
]) :: :ok

flow_index_new()

@spec flow_index_new() :: flow_index_resource()

flow_index_put_entries(resource, entries)

@spec flow_index_put_entries(flow_index_resource(), [{binary(), binary(), float()}]) ::
  :ok

flow_index_put_new_entries(resource, entries)

@spec flow_index_put_new_entries(flow_index_resource(), [
  {binary(), binary(), float()}
]) :: :ok

flow_index_range_after_slice(resource, key, min_kind, min_score, max_kind, max_score, cursor_score, cursor_member, offset, count)

@spec flow_index_range_after_slice(
  flow_index_resource(),
  binary(),
  non_neg_integer(),
  float(),
  non_neg_integer(),
  float(),
  float(),
  binary(),
  non_neg_integer(),
  non_neg_integer()
) :: [{binary(), float()}]

flow_index_range_cursor_slice(resource, key, min_kind, min_score, max_kind, max_score, cursor_score, cursor_member, offset, count)

@spec flow_index_range_cursor_slice(
  flow_index_resource(),
  binary(),
  non_neg_integer(),
  float(),
  non_neg_integer(),
  float(),
  float(),
  binary(),
  non_neg_integer(),
  non_neg_integer()
) :: [{binary(), float()}]

flow_index_range_slice(resource, key, min_kind, min_score, max_kind, max_score, reverse?, offset, count)

@spec flow_index_range_slice(
  flow_index_resource(),
  binary(),
  non_neg_integer(),
  float(),
  non_neg_integer(),
  float(),
  boolean(),
  non_neg_integer(),
  non_neg_integer()
) :: [{binary(), float()}]

flow_index_restore_count(resource, key, count)

@spec flow_index_restore_count(flow_index_resource(), binary(), integer()) :: :ok

flow_index_rollback_claim_entries(resource, entries)

@spec flow_index_rollback_claim_entries(flow_index_resource(), [
  flow_index_claim_entry()
]) :: :ok

flow_index_score_of(resource, key, member)

@spec flow_index_score_of(flow_index_resource(), binary(), binary()) ::
  {:ok, float()} | :miss

flow_index_take_due(resource, key, max_score, count)

@spec flow_index_take_due(
  flow_index_resource(),
  binary(),
  float(),
  non_neg_integer()
) :: [{binary(), float()}]

flow_record_decode(value)

@spec flow_record_decode(binary()) :: {:ok, list()} | :error

flow_record_decode_meta(value)

@spec flow_record_decode_meta(binary()) :: {:ok, list()} | :error

flow_record_encode(id, type, state, version, attempts, fencing_token, created_at_ms, updated_at_ms, next_run_at_ms, priority, ttl_ms, history_hot_max_events, history_max_events, retention_ttl_ms, terminal_retention_until_ms, max_active_ms, partition_key, payload_ref, parent_flow_id, parent_partition_key, root_flow_id, correlation_id, result_ref, error_ref, lease_owner, lease_token, lease_deadline_ms, run_state, rewound_to_event_id, child_groups_encoded)

@spec flow_record_encode(
  binary() | nil,
  binary() | nil,
  binary() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  non_neg_integer() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  binary() | nil,
  non_neg_integer() | nil,
  binary() | nil,
  binary() | nil,
  binary()
) :: binary()

flow_record_plan_claims(candidates, values, type, expected_state, worker, lease_ms, now_ms, remaining, from_due_key, to_due_key, from_state_key, to_state_key, inflight_key, worker_key, state_key_prefix)

@spec flow_record_plan_claims(
  [{binary(), float()}],
  [binary() | nil],
  binary(),
  binary(),
  binary(),
  non_neg_integer(),
  non_neg_integer(),
  non_neg_integer(),
  binary(),
  binary(),
  binary(),
  binary(),
  binary(),
  binary(),
  binary()
) ::
  {:ok, [flow_record_claim_plan()], [binary()], non_neg_integer()} | :fallback

flow_record_plan_claims_with_history(candidates, values, type, expected_state, worker, lease_ms, now_ms, remaining, from_due_key, to_due_key, from_state_key, to_state_key, inflight_key, worker_key, state_key_prefix, history_key_prefix)

@spec flow_record_plan_claims_with_history(
  [{binary(), float()}],
  [binary() | nil],
  binary(),
  binary(),
  binary(),
  non_neg_integer(),
  non_neg_integer(),
  non_neg_integer(),
  binary(),
  binary(),
  binary(),
  binary(),
  binary(),
  binary(),
  binary(),
  binary()
) ::
  {:ok, [flow_record_claim_history_plan()], [binary()], non_neg_integer()}
  | :fallback

flow_records_decode(values)

@spec flow_records_decode([binary()]) :: {:ok, [list()]} | :error

flow_records_terminal_after_noop(values)

@spec flow_records_terminal_after_noop([binary()]) :: [boolean()]

fs_append_sync_nofollow(path, payload)

@spec fs_append_sync_nofollow(binary(), binary()) :: :ok | fs_error()

Append one binary and fdatasync it without following a final symlink.

fs_append_sync_nofollow_bounded(path, payload, max_bytes)

@spec fs_append_sync_nofollow_bounded(binary(), binary(), non_neg_integer()) ::
  :ok | fs_error()

Append and fdatasync only when the opened regular file stays within max_bytes.

fs_atomic_replace_nofollow(path, payload, max_bytes)

@spec fs_atomic_replace_nofollow(binary(), binary(), non_neg_integer()) ::
  :ok | fs_error()

Durably replace a file atomically when the payload is within max_bytes.

fs_copy_replace_sync_nofollow(source, dest)

@spec fs_copy_replace_sync_nofollow(binary(), binary()) :: :ok | fs_error()

Stream-copy a locked regular file and atomically replace the destination.

fs_copy_sync_nofollow(source, dest)

@spec fs_copy_sync_nofollow(binary(), binary()) :: :ok | fs_error()

Stream-copy a regular file into a new destination without following final symlinks.

fs_exists(path)

@spec fs_exists(binary()) :: boolean()

Does the path exist? Follows symlinks (broken symlink → false). Never returns an error for the common kinds (missing, no-permission).

fs_is_dir(path)

@spec fs_is_dir(binary()) :: boolean()

Is the path a directory? Follows symlinks. Returns false for missing paths (matches File.dir?/1).

fs_ls(path)

@spec fs_ls(binary()) :: {:ok, [binary()]} | fs_error()

List the entries in a directory (names only, no path prefix). Yields every 256 entries to keep BEAM reductions accurate on huge directories.

fs_mkdir_p(path)

@spec fs_mkdir_p(binary()) :: :ok | fs_error()

Recursive mkdir -p. Idempotent when the directory already exists. Equivalent to File.mkdir_p/1.

fs_read_nofollow(path, max_bytes)

@spec fs_read_nofollow(binary(), non_neg_integer()) :: {:ok, binary()} | fs_error()

Read at most max_bytes from a regular file while refusing a symlink at the final path component. The size is checked before the BEAM binary is allocated.

fs_read_private_nofollow(path, max_bytes)

@spec fs_read_private_nofollow(binary(), non_neg_integer()) ::
  {:ok, binary()} | fs_error()

Read a private regular file without following a final symlink.

fs_rename(old_path, new_path)

@spec fs_rename(binary(), binary()) :: :ok | fs_error()

Atomic rename. On POSIX, rename replaces the target atomically. Cross-device renames return {:error, {:other, _}} — caller must handle with copy+remove.

fs_rm(path)

@spec fs_rm(binary()) :: :ok | fs_error()

Remove a single file. Use fs_rm_rf_async/3 for directory trees.

fs_rm_rf_async(caller_pid, correlation_id, path)

@spec fs_rm_rf_async(pid(), term(), binary()) :: :ok | fs_error()

Async recursive remove. Runs on the Tokio blocking pool; sends {:tokio_complete, correlation_id, :ok} or {:tokio_complete, correlation_id, :error, {kind, msg}} to the caller on completion. Idempotent: removing a non-existent path sends :ok.

fs_touch(path)

@spec fs_touch(binary()) :: :ok | fs_error()

Creates an empty file if it does not exist. Idempotent on an existing file (does not truncate). Equivalent to File.touch!/1 without the timestamp update (matches our callers' usage).

io_uring_available()

@spec io_uring_available() :: boolean()

lmdb_clear(path, map_size)

@spec lmdb_clear(binary(), non_neg_integer()) :: :ok | {:error, term()}

lmdb_composite_range_entries_bounded(path, prefix, after_key, before_key, max_items, max_bytes, map_size)

@spec lmdb_composite_range_entries_bounded(
  binary(),
  binary(),
  binary(),
  binary(),
  pos_integer(),
  pos_integer(),
  non_neg_integer()
) ::
  {:ok,
   [
     {binary(), binary(), binary(), non_neg_integer(), non_neg_integer(),
      pos_integer()}
   ], boolean(), non_neg_integer()}
  | {:error, term()}

lmdb_delete(path, key, map_size)

@spec lmdb_delete(binary(), binary(), non_neg_integer()) :: :ok | {:error, term()}

lmdb_get(path, key, map_size)

@spec lmdb_get(binary(), binary(), non_neg_integer()) ::
  {:ok, binary()} | :not_found | {:error, term()}

lmdb_get_many(path, keys, map_size)

@spec lmdb_get_many(binary(), [binary()], non_neg_integer()) ::
  {:ok, [{:ok, binary()} | :not_found]} | {:error, term()}

lmdb_get_many_bounded(path, keys, max_bytes, map_size)

@spec lmdb_get_many_bounded(binary(), [binary()], pos_integer(), non_neg_integer()) ::
  {:ok, [{:ok, binary()} | :not_found], non_neg_integer()} | {:error, term()}

lmdb_get_many_prefix_bounded(path, keys, max_bytes, map_size)

@spec lmdb_get_many_prefix_bounded(
  binary(),
  [binary()],
  pos_integer(),
  non_neg_integer()
) ::
  {:ok, [{:ok, binary()} | :not_found], non_neg_integer(), boolean()}
  | {:error, term()}

lmdb_prefix_count(path, prefix, map_size)

@spec lmdb_prefix_count(binary(), binary(), non_neg_integer()) ::
  {:ok, non_neg_integer()} | {:error, term()}

lmdb_prefix_entries(path, prefix, limit, map_size)

@spec lmdb_prefix_entries(binary(), binary(), non_neg_integer(), non_neg_integer()) ::
  {:ok, [{binary(), binary()}]} | {:error, term()}

lmdb_prefix_entries_after(path, prefix, after_key, limit, map_size)

@spec lmdb_prefix_entries_after(
  binary(),
  binary(),
  binary(),
  non_neg_integer(),
  non_neg_integer()
) :: {:ok, [{binary(), binary()}]} | {:error, term()}

lmdb_prefix_entries_after_bounded(path, prefix, after_key, max_items, max_bytes, map_size)

@spec lmdb_prefix_entries_after_bounded(
  binary(),
  binary(),
  binary(),
  non_neg_integer(),
  non_neg_integer(),
  non_neg_integer()
) :: {:ok, [{binary(), binary()}]} | {:error, term()}

lmdb_prefix_entries_reverse(path, prefix, limit, map_size)

@spec lmdb_prefix_entries_reverse(
  binary(),
  binary(),
  non_neg_integer(),
  non_neg_integer()
) ::
  {:ok, [{binary(), binary()}]} | {:error, term()}

lmdb_prefix_entries_reverse_before(path, prefix, before_key, limit, map_size)

@spec lmdb_prefix_entries_reverse_before(
  binary(),
  binary(),
  binary(),
  non_neg_integer(),
  non_neg_integer()
) :: {:ok, [{binary(), binary()}]} | {:error, term()}

lmdb_prefix_merge_entries(paths, prefix, limit, max_bytes, map_size)

@spec lmdb_prefix_merge_entries(
  [binary()],
  binary(),
  non_neg_integer(),
  pos_integer(),
  non_neg_integer()
) ::
  {:ok, [{non_neg_integer(), binary(), binary()}], non_neg_integer()}
  | {:error, term()}

lmdb_put(path, key, value, map_size)

@spec lmdb_put(binary(), binary(), binary(), non_neg_integer()) ::
  :ok | {:error, term()}

lmdb_range_entries_bounded(path, prefix, after_key, before_key, max_items, max_bytes, map_size)

@spec lmdb_range_entries_bounded(
  binary(),
  binary(),
  binary(),
  binary(),
  pos_integer(),
  pos_integer(),
  non_neg_integer()
) ::
  {:ok, [{binary(), binary()}], boolean(), non_neg_integer()} | {:error, term()}

lmdb_release(path)

@spec lmdb_release(binary()) ::
  {:ok, non_neg_integer()} | {:busy, non_neg_integer()} | {:error, term()}

lmdb_release_all()

@spec lmdb_release_all() ::
  {:ok, non_neg_integer()} | {:busy, non_neg_integer()} | {:error, term()}

lmdb_write_batch(path, ops, map_size)

@spec lmdb_write_batch(binary(), [lmdb_op()], non_neg_integer()) ::
  :ok | {:error, term()}

lmdb_write_batch_with_originals(path, ops, map_size)

@spec lmdb_write_batch_with_originals(binary(), [lmdb_op()], non_neg_integer()) ::
  {:ok, [lmdb_original()]} | {:error, term()}

prob_file_recover(path, extension)

@spec prob_file_recover(binary(), binary()) :: :ok | {:error, term()}

rust_allocated_bytes()

@spec rust_allocated_bytes() :: {:ok, non_neg_integer()} | {:error, term()}

topk_file_add_v2(path, elements)

@spec topk_file_add_v2(binary(), [binary()]) :: [binary() | nil] | {:error, term()}

topk_file_add_v2_at(path, receipt_path, elements, mutation_index, mutation_ordinal)

@spec topk_file_add_v2_at(
  binary(),
  binary(),
  [binary()],
  non_neg_integer(),
  pos_integer()
) :: [binary() | nil] | {:error, term()}

topk_file_count_v2(path, elements)

@spec topk_file_count_v2(binary(), [binary()]) ::
  {:ok, [non_neg_integer()]} | {:error, term()}

topk_file_count_v2_async(caller_pid, correlation_id, path, elements)

@spec topk_file_count_v2_async(pid(), term(), binary(), [binary()]) ::
  :ok | {:error, term()}

topk_file_create_v2(path, k, width, depth)

@spec topk_file_create_v2(
  binary(),
  non_neg_integer(),
  non_neg_integer(),
  non_neg_integer()
) :: :ok | {:error, term()}

topk_file_incrby_v2(path, pairs)

@spec topk_file_incrby_v2(binary(), [{binary(), non_neg_integer()}]) ::
  [binary() | nil] | {:error, term()}

topk_file_incrby_v2_at(path, receipt_path, pairs, mutation_index, mutation_ordinal)

@spec topk_file_incrby_v2_at(
  binary(),
  binary(),
  [{binary(), non_neg_integer()}],
  non_neg_integer(),
  pos_integer()
) :: [binary() | nil] | {:error, term()}

topk_file_info_v2(path)

@spec topk_file_info_v2(binary()) ::
  {pos_integer(), pos_integer(), pos_integer()} | {:error, term()}

topk_file_info_v2_async(caller_pid, correlation_id, path)

@spec topk_file_info_v2_async(pid(), term(), binary()) :: :ok | {:error, term()}

topk_file_list_v2(path)

@spec topk_file_list_v2(binary()) ::
  {:ok, [{binary(), non_neg_integer()}]} | {:error, term()}

topk_file_list_v2_async(caller_pid, correlation_id, path)

@spec topk_file_list_v2_async(pid(), term(), binary()) :: :ok | {:error, term()}

topk_file_list_with_count(path)

@spec topk_file_list_with_count(binary()) ::
  [binary() | non_neg_integer()] | {:error, term()}

topk_file_list_with_count_async(caller_pid, correlation_id, path)

@spec topk_file_list_with_count_async(pid(), term(), binary()) ::
  :ok | {:error, term()}

topk_file_query_v2(path, elements)

@spec topk_file_query_v2(binary(), [binary()]) ::
  {:ok, [boolean()]} | {:error, term()}

topk_file_query_v2_async(caller_pid, correlation_id, path, elements)

@spec topk_file_query_v2_async(pid(), term(), binary(), [binary()]) ::
  :ok | {:error, term()}

v2_append_batch(path, records)

@spec v2_append_batch(binary(), [{binary(), binary(), non_neg_integer()}]) ::
  {:ok, [{non_neg_integer(), non_neg_integer()}]} | {:error, term()}

v2_append_batch_async(caller_pid, correlation_id, path, records)

@spec v2_append_batch_async(pid(), term(), binary(), [
  {binary(), binary(), non_neg_integer()}
]) ::
  :ok | {:error, term()}

v2_append_batch_nosync(path, records)

@spec v2_append_batch_nosync(binary(), [{binary(), binary(), non_neg_integer()}]) ::
  {:ok, [{non_neg_integer(), non_neg_integer()}]} | {:error, term()}

v2_append_ops_batch(path, records)

@spec v2_append_ops_batch(binary(), [append_op()]) ::
  {:ok, [append_op_location()]} | {:error, term()}

v2_append_ops_batch_nosync(path, records)

@spec v2_append_ops_batch_nosync(binary(), [append_op()]) ::
  {:ok, [append_op_location()]} | {:error, term()}

v2_append_record(path, key, value, expire_at_ms)

@spec v2_append_record(binary(), binary(), binary(), non_neg_integer()) ::
  {:ok, {non_neg_integer(), non_neg_integer()}} | {:error, term()}

v2_append_tombstone(path, key)

@spec v2_append_tombstone(binary(), binary()) ::
  {:ok, {non_neg_integer(), non_neg_integer()}} | {:error, term()}

v2_available_disk_space(path)

@spec v2_available_disk_space(binary()) :: {:ok, non_neg_integer()} | {:error, term()}

v2_build_hint_file_from_log(log_path, hint_path, file_id)

@spec v2_build_hint_file_from_log(binary(), binary(), non_neg_integer()) ::
  {:ok, non_neg_integer(), non_neg_integer()} | {:error, term()}

v2_copy_records(source_path, dest_path, offsets)

@spec v2_copy_records(binary(), binary(), [non_neg_integer()]) ::
  {:ok, [{non_neg_integer(), non_neg_integer()}]} | {:error, term()}

v2_copy_records_preserve_tombstones(source_path, dest_path, live_offsets, tombstone_offsets)

@spec v2_copy_records_preserve_tombstones(binary(), binary(), [non_neg_integer()], [
  non_neg_integer()
]) ::
  {:ok, [{non_neg_integer(), non_neg_integer()}]} | {:error, term()}

v2_fsync(path)

@spec v2_fsync(binary()) :: :ok | {:error, term()}

v2_fsync_async(caller_pid, correlation_id, path)

@spec v2_fsync_async(pid(), term(), binary()) :: :ok | {:error, term()}

v2_fsync_dir(path)

@spec v2_fsync_dir(binary()) :: :ok | {:error, term()}

Fsyncs a directory so that recent File.rename/2, File.rm/1, File.touch!/1, or File.create/1 operations on files inside it are durable. Use after any namespace mutation that must survive a kernel panic — rotation, compaction rename, hint-file creation, prob-file create/delete, shard init.

Returns :ok on success or {:error, reason} where reason is a short string suitable for logging.

POSIX: file-data fsync does NOT make the filename durable; only a directory fsync does.

v2_pread_at(path, offset)

@spec v2_pread_at(binary(), non_neg_integer()) :: {:ok, binary()} | {:error, term()}

v2_pread_at_async(caller_pid, correlation_id, path, offset)

@spec v2_pread_at_async(pid(), term(), binary(), non_neg_integer()) ::
  :ok | {:error, term()}

v2_pread_at_key_async(caller_pid, correlation_id, path, offset, expected_key)

@spec v2_pread_at_key_async(pid(), term(), binary(), non_neg_integer(), binary()) ::
  :ok | {:error, term()}

v2_pread_batch(path, locations)

@spec v2_pread_batch(binary(), [non_neg_integer()]) ::
  {:ok, [binary() | nil]} | {:error, term()}

v2_pread_batch_async(caller_pid, correlation_id, locations)

@spec v2_pread_batch_async(pid(), term(), [{binary(), non_neg_integer()}]) ::
  :ok | {:error, term()}

v2_pread_batch_grouped_async(caller_pid, correlation_id, groups)

@spec v2_pread_batch_grouped_async(
  pid(),
  term(),
  [{binary(), [{non_neg_integer(), non_neg_integer()}]}]
) :: :ok | {:error, term()}

v2_pread_batch_grouped_key_async(caller_pid, correlation_id, groups)

@spec v2_pread_batch_grouped_key_async(
  pid(),
  term(),
  [{binary(), [{non_neg_integer(), non_neg_integer(), binary()}]}]
) :: :ok | {:error, term()}

v2_pread_batch_path_async(caller_pid, correlation_id, path, offsets)

@spec v2_pread_batch_path_async(pid(), term(), binary(), [non_neg_integer()]) ::
  :ok | {:error, term()}

v2_pread_batch_path_key_async(caller_pid, correlation_id, path, reads)

@spec v2_pread_batch_path_key_async(
  pid(),
  term(),
  binary(),
  [{non_neg_integer(), binary()}]
) :: :ok | {:error, term()}

v2_read_hint_file_page(path, start_offset, max_entries, max_bytes)

@spec v2_read_hint_file_page(
  binary(),
  non_neg_integer(),
  pos_integer(),
  pos_integer()
) ::
  {:ok,
   [
     {binary(), non_neg_integer(), non_neg_integer(), non_neg_integer(),
      non_neg_integer()}
   ], non_neg_integer(), boolean()}
  | {:error, term()}

v2_scan_file_page(path, start_offset, limit)

@spec v2_scan_file_page(binary(), non_neg_integer(), pos_integer()) ::
  {:ok,
   [
     {binary(), non_neg_integer(), non_neg_integer(), non_neg_integer(),
      boolean()}
   ], non_neg_integer(), boolean()}
  | {:error, term()}

v2_scan_key_states(path, keys)

@spec v2_scan_key_states(binary(), [binary()]) ::
  {:ok, [{binary(), non_neg_integer(), boolean()}]} | {:error, term()}

v2_scan_tombstones_page(path, start_offset, max_records)

@spec v2_scan_tombstones_page(binary(), non_neg_integer(), non_neg_integer()) ::
  {:ok, [{binary(), non_neg_integer(), non_neg_integer(), non_neg_integer()}],
   non_neg_integer(), boolean()}
  | {:error, term()}

Strictly scans at most max_records physical log records for tombstones.

The returned cursor points to the next physical record. done is true only when that cursor reaches the file length captured when the page was opened.

v2_validate_value_ref(path, offset, expected_key, expected_value_size)

@spec v2_validate_value_ref(binary(), non_neg_integer(), binary(), non_neg_integer()) ::
  {:ok, {non_neg_integer(), non_neg_integer()}} | :mismatch | {:error, term()}

v2_write_hint_file(path, entries)

@spec v2_write_hint_file(binary(), [
  {binary(), non_neg_integer(), non_neg_integer(), non_neg_integer(),
   non_neg_integer()}
]) :: :ok | {:error, term()}