Skip to content
Merged
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Special thanks to all contributors who made v1.0.0 possible through testing, fee

Planned for v1.1.0:
- Additional cloud storage backend optimizations
- Enhanced v3 format support
- Zarr v3 extension features (variable chunking, additional codecs)
- Performance improvements for large arrays
- Additional convenience functions for common patterns

Expand Down
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@

Elixir implementation of [Zarr](https://zarr.dev): compressed, chunked, N-dimensional arrays designed for parallel computing and scientific data storage.

**Full Zarr v3 Support:** ExZarr implements both Zarr v2 and v3 specifications with production-ready support for v3's unified codec pipeline, improved metadata format, and modern features. Automatic version detection ensures seamless interoperability. See [ZARR_V3_STATUS.md](ZARR_V3_STATUS.md) for complete v3 support details.

## Features

- **Zarr v3 and v2 Support** - Full implementation of both specifications with automatic version detection
- **High Performance** - 26x faster multi-chunk reads with near-optimal scaling (see [Performance Guide](guides/performance.md))
- **N-dimensional arrays** with support for 10 data types (int8-64, uint8-64, float32/64)
- **Parallel chunk processing** - Automatic parallel I/O and decompression for large operations
Expand All @@ -19,8 +22,7 @@ Elixir implementation of [Zarr](https://zarr.dev): compressed, chunked, N-dimens
- **Flexible storage** backends (in-memory, filesystem, and zip archive)
- **Custom storage backends** with plugin architecture for S3, databases, and more
- **Hierarchical groups** for organizing multiple arrays
- **Zarr v2 and v3 specification** support with automatic version detection
- **Full backward compatibility** - seamlessly work with both v2 and v3 arrays
- **Full Python interoperability** - Read and write arrays compatible with zarr-python 2.x and 3.x
- **Property-based testing** with comprehensive test coverage

## Installation
Expand All @@ -40,12 +42,26 @@ end
### Creating an Array

```elixir
# Create a 2D array in memory
# Create a Zarr v3 array (recommended for new projects)
{:ok, array} = ExZarr.create(
shape: {1000, 1000},
chunks: {100, 100},
dtype: :float64,
codecs: [
%{name: "bytes"},
%{name: "gzip", configuration: %{level: 5}}
],
zarr_version: 3,
storage: :memory
)

# Or use v2 format for compatibility with older tools
{:ok, array_v2} = ExZarr.create(
shape: {1000, 1000},
chunks: {100, 100},
dtype: :float64,
compressor: :zlib,
zarr_version: 2,
storage: :memory
)
```
Expand Down Expand Up @@ -86,11 +102,11 @@ mix run benchmarks/slicing_bench_quick.exs

## Zarr Format Support

ExZarr supports both Zarr v2 and v3 specifications. Arrays can be created in either format, and opening arrays automatically detects the version.
ExZarr provides production-ready support for both Zarr v2 and v3 specifications. Arrays can be created in either format, and opening arrays automatically detects the version.

### Zarr v3 (Recommended for New Projects)
### Zarr v3 - Fully Supported (Recommended for New Projects)

Zarr v3 introduces a unified codec pipeline and improved metadata format:
Zarr v3 is fully implemented with a unified codec pipeline and improved metadata format:

```elixir
# Create v3 array with unified codec pipeline
Expand Down
181 changes: 181 additions & 0 deletions ZARR_V3_STATUS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# Zarr v3 Support Status

## TL;DR

**ExZarr provides full, production-ready support for Zarr v3.**

Both Zarr v2 and v3 are first-class citizens in ExZarr with complete implementations and automatic version detection.

## Production Status

| Feature | Status | Notes |
|---------|--------|-------|
| **Zarr v3 Core Specification** | Production-ready | Full implementation |
| **Unified Codec Pipeline** | Production-ready | Complete support |
| **v3 Metadata Format** | Production-ready | `zarr.json` with embedded attributes |
| **Hierarchical Chunk Storage** | Production-ready | `c/` directory with slash-separated keys |
| **Automatic Version Detection** | Production-ready | Transparent v2/v3 interoperability |
| **Python Interoperability** | Production-ready | Compatible with zarr-python 3.x |
| **Dimension Names** | Production-ready | Full support |
| **Custom Chunk Grids** | Production-ready | Regular and irregular grids |
| **Sharding Extension** | Supported | Optional v3 extension |

## What This Means

### For New Projects
**Recommended:** Use Zarr v3 as the default for new projects.

```elixir
{:ok, array} = ExZarr.create(
shape: {1000, 1000},
chunks: {100, 100},
dtype: :float64,
codecs: [
%{name: "bytes"},
%{name: "gzip", configuration: %{level: 5}}
],
zarr_version: 3,
storage: :filesystem,
path: "/data/my_array"
)
```

### For Existing Projects
- v2 arrays continue to work perfectly
- v3 arrays are fully supported
- Automatic version detection handles both formats
- Gradual migration is straightforward

### For Python Interoperability
- **zarr-python 3.x:** Full interoperability with v3 arrays
- **zarr-python 2.x:** Use v2 format for compatibility
- Both versions are production-ready in ExZarr

## Features Comparison

### Zarr v3 Advantages
- Unified codec pipeline (explicit, configurable)
- Improved metadata format (single `zarr.json` file)
- Better extensibility (built-in extension support)
- Dimension names (semantic axis labels)
- Hierarchical chunk storage (cleaner organization)
- Sharding support (reduce object count in cloud storage)

### Zarr v2 Advantages
- Broader ecosystem compatibility (older tools)
- Maximum compatibility with zarr-python 2.x
- Widely deployed in existing systems

## Implementation Details

### What's Fully Implemented (v3)
- [x] Core v3 specification
- [x] Unified codec pipeline
- [x] v3 metadata format (`zarr.json`)
- [x] Hierarchical chunk keys (`c/0/1/2`)
- [x] Automatic version detection
- [x] Dimension names
- [x] Custom chunk grids
- [x] Array-to-array codecs
- [x] Array-to-bytes codecs
- [x] Bytes-to-bytes codecs
- [x] Groups with v3 format
- [x] Attributes in metadata
- [x] Python zarr-python 3.x interoperability

### Extension Features
- [x] Sharding (optional extension)
- [ ] Variable chunking (future extension)
- [ ] Custom extension support (extensible architecture)

## Version Selection Guide

### Choose Zarr v3 When:
- Starting a new project (recommended default)
- Working with modern Python tools (zarr-python 3.x)
- Need improved metadata organization
- Want unified codec pipeline flexibility
- Require dimension names
- Cloud-native workflows benefiting from sharding

### Choose Zarr v2 When:
- Maximum compatibility with legacy tools required
- Working with older Python codebases (zarr-python 2.x)
- Sharing data with users on older platforms
- Legacy tool compatibility more important than modern features

## Testing and Validation

ExZarr's v3 implementation is validated through:
- **Specification compliance:** Implements zarr-specs v3 specification
- **Python interoperability tests:** Round-trip testing with zarr-python 3.x
- **Property-based tests:** Randomized validation of v3 features
- **Integration tests:** Real-world v3 workflows
- **Compatibility tests:** v2 and v3 coexistence

## Migration Path

v2 to v3 migration is straightforward:

```elixir
# Open existing v2 array
{:ok, v2_array} = ExZarr.open(path: "/data/v2_array")

# Create new v3 array with same data
{:ok, v3_array} = ExZarr.create(
shape: v2_array.shape,
chunks: v2_array.chunks,
dtype: v2_array.dtype,
codecs: [
%{name: "bytes"},
%{name: "gzip", configuration: %{level: 5}}
],
zarr_version: 3,
storage: :filesystem,
path: "/data/v3_array"
)

# Copy data
{:ok, data} = ExZarr.fetch_all(v2_array)
:ok = ExZarr.store_all(v3_array, data)
```

See [docs/V2_TO_V3_MIGRATION.md](docs/V2_TO_V3_MIGRATION.md) for detailed guidance.

## Frequently Asked Questions

### Is v3 stable enough for production?
**Yes.** ExZarr's v3 implementation is production-ready with comprehensive testing and validation.

### Should I use v3 for new projects?
**Yes.** v3 is the modern standard and recommended for new projects.

### Can I mix v2 and v3 arrays?
**Yes.** ExZarr handles both formats transparently with automatic version detection.

### What about Python compatibility?
**Full compatibility** with zarr-python 3.x for v3 arrays, and zarr-python 2.x for v2 arrays.

### Is v3 faster than v2?
Performance is similar for core operations. v3's sharding extension can significantly reduce cloud storage API calls.

### When will v3 be the default?
v3 is fully supported now. We recommend it for new projects. The default will shift to v3 in a future release once ecosystem adoption increases.

## Resources

- [Zarr v3 Specification](https://zarr-specs.readthedocs.io/en/latest/v3/core/v3.0.html)
- [V2 to V3 Migration Guide](docs/V2_TO_V3_MIGRATION.md)
- [Python Interoperability Guide](guides/python_interop.md)
- [Core Concepts Guide](guides/core_concepts.md)

## Questions or Issues?

If you encounter any v3-related issues or have questions:
- Open an issue: https://github.com/thanos/ExZarr/issues
- Check documentation: https://hexdocs.pm/ex_zarr
- Review examples: `examples/` directory

## Summary

ExZarr's Zarr v3 support is **complete, tested, and production-ready**. Both v2 and v3 are first-class formats with full feature parity and automatic version detection. Use v3 for new projects and enjoy modern Zarr features with confidence.
138 changes: 138 additions & 0 deletions docs/articles/medium/exzarr-101-cloud-native-arrays-in-elixir.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# ExZarr 101: Cloud‑native arrays in Elixir with Zarr v2

Zarr is a storage format (and set of conventions) for **chunked, compressed, N‑dimensional arrays** that works well on local disks *and* on object stores (S3/GCS/Azure). ExZarr brings that model to Elixir so you can combine:

- Zarr’s partial I/O (read/write just the chunks you need)
- Elixir/OTP concurrency (process many chunks in parallel)
- fault tolerance (supervise ingestion pipelines, retry failures safely)

This post is a practical walk-through. It mirrors the runnable Livebook:
`livebooks/01_core_zarr/01_01_first_zarr_array.livemd`.

---

## What you’ll build

1) Create a Zarr array (in memory and on disk)
2) Write a slice (rectangular region)
3) Read a slice back
4) Stream chunks (sequential + parallel)
5) Save metadata and reopen the array

---

## The mental model

A Zarr array has:

- `shape` — total dimensions, e.g. `{1000, 1000}`
- `chunks` — chunk dimensions, e.g. `{100, 100}`
- `dtype` — element type, e.g. `:float32`
- `compressor` — e.g. `:zstd`, `:zlib`, `:lz4`, or `:none`
- `storage` — in memory or filesystem (and later: object stores)

When you read a slice, ExZarr loads and decompresses only the chunks that overlap that region.

---

## Quickstart: create a 2D array

```elixir
{:ok, a} =
ExZarr.Array.create(
shape: {1000, 1000},
chunks: {100, 100},
dtype: :int32,
compressor: :zstd,
storage: :memory
)
```

At this point you have an array handle. Data is created lazily when you write.

---

## Writing a slice

ExZarr writes raw binaries in row-major order. For a `10x10` region of `:int32`,
you need `10 * 10 * 4 = 400` bytes.

In the Livebook we use a tiny helper `ExZarr.Gallery.Pack` to pack numbers to binary.

```elixir
data = ExZarr.Gallery.Pack.pack(Enum.to_list(1..100), :int32)

:ok =
ExZarr.Array.set_slice(a, data,
start: {0, 0},
stop: {10, 10}
)
```

---

## Reading it back

```elixir
{:ok, bin} =
ExZarr.Array.get_slice(a,
start: {0, 0},
stop: {10, 10}
)

vals = ExZarr.Gallery.Pack.unpack(bin, :int32)
```

Now `vals` is a flat list (row-major).

---

## Streaming chunks

Chunk streaming is where Zarr starts to feel “cloud native”: you can process an array without loading it all.

```elixir
ExZarr.Array.chunk_stream(a, parallel: 4, ordered: false)
|> Stream.each(fn {chunk_index, bin} ->
# do something with each chunk
:ok
end)
|> Stream.run()
```

This is a nice fit for OTP pipelines: bounded concurrency, progress callbacks, and chunk-level isolation.

---

## Saving to disk and reopening

```elixir
path = "/tmp/exzarr_demo/array_2d"
File.rm_rf!(path)
File.mkdir_p!(path)

:ok = ExZarr.Array.save(a, path: path)

{:ok, reopened} = ExZarr.Array.open(path: path)
```

Once saved, you can reopen from other processes (or other languages that implement Zarr v2).

---

## Next steps

If you liked this, the next content in the ExZarr gallery focuses on:

- chunk sizing heuristics (the #1 performance lever)
- compression tradeoffs (CPU vs IO)
- parallel ingestion patterns with backpressure
- AI/GenAI datasets (embeddings, RAG caches, training loaders)
- finance datasets (tick cubes, order books, risk grids)

---

## Links

- ExZarr docs: https://hexdocs.pm/ex_zarr/ExZarr.Array.html
- Zarr v2 spec: https://zarr-specs.readthedocs.io/en/latest/v2/v2.0.html
Loading
Loading