ExZarr.ArrayServer (ExZarr v1.1.0)

View Source

GenServer for coordinating concurrent access to Zarr arrays.

Provides chunk-level locking to prevent write conflicts while allowing concurrent reads. Monitors processes holding locks and automatically cleans up on process crashes.

Features

  • Chunk-level read/write locks
  • Multiple concurrent readers
  • Exclusive writers
  • Automatic lock cleanup on process crash
  • Deadlock prevention via timeouts
  • Process monitoring

Lock Semantics

  • Read locks: Multiple processes can hold read locks on the same chunk simultaneously
  • Write locks: Only one process can hold a write lock on a chunk at a time
  • A chunk with a write lock cannot have any read locks (and vice versa)
  • Locks are automatically released when the process terminates

Usage

# Start server for an array
{:ok, pid} = ExZarr.ArrayServer.start_link(array_id: "my_array")

# Acquire write lock on chunk {0, 0}
:ok = ExZarr.ArrayServer.lock_chunk(pid, {0, 0}, :write)

# Do write operation...

# Release lock
:ok = ExZarr.ArrayServer.unlock_chunk(pid, {0, 0})

# Acquire read lock (multiple processes can do this)
:ok = ExZarr.ArrayServer.lock_chunk(pid, {0, 0}, :read)

Summary

Functions

Returns a specification to start this module under a supervisor.

Returns information about all current locks.

Releases all locks held by the calling process.

Starts the ArrayServer for coordinating access to an array.

Attempts to acquire a lock without blocking.

Releases a lock on a chunk.

Types

chunk_index()

@type chunk_index() :: tuple()

lock_timeout()

@type lock_timeout() :: timeout()

lock_type()

@type lock_type() :: :read | :write

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

get_locks(server)

@spec get_locks(GenServer.server()) :: map()

Returns information about all current locks.

Useful for debugging and monitoring.

lock_chunk(server, chunk_index, lock_type, timeout \\ 5000)

@spec lock_chunk(GenServer.server(), chunk_index(), lock_type(), lock_timeout()) ::
  :ok | {:error, term()}

Acquires a lock on a specific chunk.

Parameters

  • server - The ArrayServer process
  • chunk_index - Tuple identifying the chunk (e.g., {0, 0})
  • lock_type - Either :read or :write
  • timeout - Maximum time to wait for lock (default: 5000ms)

Returns

  • :ok if lock acquired
  • {:error, :timeout} if lock couldn't be acquired within timeout
  • {:error, reason} for other errors

release_all_locks(server)

@spec release_all_locks(GenServer.server()) :: :ok

Releases all locks held by the calling process.

Useful for cleanup in error scenarios.

start_link(opts)

@spec start_link(keyword()) :: GenServer.on_start()

Starts the ArrayServer for coordinating access to an array.

Options

  • :array_id - Unique identifier for the array (required)
  • :name - Process name (optional)

try_lock_chunk(server, chunk_index, lock_type)

@spec try_lock_chunk(GenServer.server(), chunk_index(), lock_type()) ::
  :ok | {:error, :locked | term()}

Attempts to acquire a lock without blocking.

Returns immediately with :ok if lock acquired or {:error, :locked} if not available.

unlock_chunk(server, chunk_index)

@spec unlock_chunk(GenServer.server(), chunk_index()) :: :ok | {:error, term()}

Releases a lock on a chunk.

The calling process must currently hold a lock on the chunk.