You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fluss.apache.org by ab...@apache.org on 2026/10/14 07:52:18 UTC
(fluss) branch main updated: [elixir] Set up HexDocs and add usage guides (#4633)
This is an automated email from the ASF dual-hosted git repository.
fresh-borzoni pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss.git
The following commit(s) were added to refs/heads/main by this push:
new 6af19e6fb1 [elixir] Set up HexDocs and add usage guides (#4633)
6af19e6fb1 is described below
commit 6af19e6fb195ca3bbb7deeef35ad4c82bc5fc1da
Author: Nicoleta Lazar <ni...@fresha.com>
AuthorDate: Wed Oct 14 08:52:13 2026 +0100
[elixir] Set up HexDocs and add usage guides (#4633)
Configure ExDoc for the Elixir client and add the first guides.
- mix.exs: README as the landing page, modules grouped by area, and
source links into the monorepo path.
- Guides: log tables (create, write, and a GenServer consumer) and data
types (Fluss type, schema atom and Elixir value, with conversions to
standard library types).
- Fluss.Admin: document that create/drop calls ignore an existing or
missing database or table by default, and that drop_database doesn't
cascade.
- Ignore the generated doc/ directory.
---
fluss-rust/bindings/elixir/.gitignore | 1 +
fluss-rust/bindings/elixir/guides/data_types.md | 46 +++++++++++++++
fluss-rust/bindings/elixir/guides/log_tables.md | 76 +++++++++++++++++++++++++
fluss-rust/bindings/elixir/lib/fluss/admin.ex | 4 ++
fluss-rust/bindings/elixir/mix.exs | 29 ++++++++++
5 files changed, 156 insertions(+)
diff --git a/fluss-rust/bindings/elixir/.gitignore b/fluss-rust/bindings/elixir/.gitignore
index dd00538b13..64d2fd4e94 100644
--- a/fluss-rust/bindings/elixir/.gitignore
+++ b/fluss-rust/bindings/elixir/.gitignore
@@ -1,6 +1,7 @@
# Elixir build artifacts
_build/
deps/
+doc/
# Generated NIF shared library
priv/native/
diff --git a/fluss-rust/bindings/elixir/guides/data_types.md b/fluss-rust/bindings/elixir/guides/data_types.md
new file mode 100644
index 0000000000..2822de6652
--- /dev/null
+++ b/fluss-rust/bindings/elixir/guides/data_types.md
@@ -0,0 +1,46 @@
+# Data types
+
+How each Fluss type is declared in a `Fluss.Schema` and represented as an Elixir value. Values
+are the same for writing and reading.
+
+| Fluss type | Schema | Elixir value |
+|---|---|---|
+| BOOLEAN | `:boolean` | `true` / `false` |
+| TINYINT, SMALLINT, INT, BIGINT | `:tinyint`, `:smallint`, `:int`, `:bigint` | integer, range-checked on write |
+| FLOAT, DOUBLE | `:float`, `:double` | float |
+| STRING | `:string` | `String.t()` |
+| CHAR(n) | `{:char, n}` | `String.t()` |
+| DECIMAL(p, s) | `{:decimal, p, s}` | string, e.g. `"12.34"`; rounded half-up to `s` on write |
+| BYTES | `:bytes` | binary |
+| BINARY(n) | `{:binary, n}` | binary |
+| DATE | `:date` | integer: days since 1970-01-01 |
+| TIME | `:time` | integer: milliseconds since midnight |
+| TIMESTAMP | `:timestamp` | `{epoch_millis, nanos_of_milli}`, no time zone |
+| TIMESTAMP_LTZ | `:timestamp_ltz` | `{epoch_millis, nanos_of_milli}`, a UTC instant |
+| ARRAY | `{:array, type}` | list |
+| MAP | `{:map, key_type, value_type}` | map |
+| ROW | `{:row, [{"field", type}]}` | map keyed by field-name atoms |
+
+Converting to standard library types:
+
+```elixir
+date = Date.add(~D[1970-01-01], days)
+time = Time.add(~T[00:00:00], millis_of_day, :millisecond)
+
+{millis, nanos} = timestamp
+utc = DateTime.add(DateTime.from_unix!(millis, :millisecond), nanos, :nanosecond) # :timestamp_ltz
+naive = DateTime.to_naive(utc) # :timestamp
+```
+
+## Nullability
+
+`nil` is null; `{:not_null, type}` forbids it (see `Fluss.Schema`). Primary key columns and map
+keys are always `NOT NULL`: `Fluss.Admin.get_table_schema/4` returns them wrapped even if
+declared bare.
+
+## Not supported yet
+
+- TIME and TIMESTAMP precision can't be declared; `:time` is TIME(0) and `:timestamp` /
+ `:timestamp_ltz` are precision 6.
+- A FLOAT or DOUBLE holding NaN or ±Infinity (writable from other clients) can't be read:
+ Erlang has no such values.
diff --git a/fluss-rust/bindings/elixir/guides/log_tables.md b/fluss-rust/bindings/elixir/guides/log_tables.md
new file mode 100644
index 0000000000..110ad02811
--- /dev/null
+++ b/fluss-rust/bindings/elixir/guides/log_tables.md
@@ -0,0 +1,76 @@
+# Log tables
+
+A log table has no primary key: records are appended and never change.
+
+## Create
+
+```elixir
+schema =
+ Fluss.Schema.new()
+ |> Fluss.Schema.column("ts", :bigint)
+ |> Fluss.Schema.column("message", :string)
+
+descriptor = Fluss.TableDescriptor.new!(schema, bucket_count: 3)
+:ok = Fluss.Admin.create_table(admin, "my_db", "events", descriptor)
+
+table = Fluss.Table.get!(conn, "my_db", "events")
+```
+
+## Write
+
+Values are a list in column order; `nil` is null. See `Fluss.AppendWriter` for per-record acks.
+
+```elixir
+writer = Fluss.AppendWriter.new!(table)
+Fluss.AppendWriter.append(writer, [1_700_000_000, "hello"])
+:ok = Fluss.AppendWriter.flush(writer)
+```
+
+## Read
+
+A scanner subscribes to buckets, each from a starting offset; `Fluss.LogScanner.poll/2`
+delivers records as messages. `{:fluss_records, []}` means nothing arrived before the timeout,
+and `:change_type` is always `:append_only` for log tables.
+
+A GenServer that reads every bucket from the start:
+
+```elixir
+defmodule MyApp.EventsConsumer do
+ use GenServer
+
+ def start_link({admin, table}), do: GenServer.start_link(__MODULE__, {admin, table})
+
+ @impl true
+ def init({admin, table}) do
+ info = Fluss.Admin.get_table_info!(admin, "my_db", "events")
+ scanner = Fluss.LogScanner.new!(table)
+ buckets = for b <- 0..(info.num_buckets - 1), do: {b, Fluss.earliest_offset()}
+ :ok = Fluss.LogScanner.subscribe_buckets(scanner, buckets)
+ send(self(), :poll)
+ {:ok, scanner}
+ end
+
+ @impl true
+ def handle_info(:poll, scanner) do
+ :ok = Fluss.LogScanner.poll(scanner, 1_000)
+ {:noreply, scanner}
+ end
+
+ def handle_info({:fluss_records, records}, scanner) do
+ Enum.each(records, &IO.inspect(&1.row))
+ send(self(), :poll)
+ {:noreply, scanner}
+ end
+
+ def handle_info({:fluss_poll_error, error}, scanner) do
+ IO.warn("poll failed: #{Exception.message(error)}")
+ send(self(), :poll)
+ {:noreply, scanner}
+ end
+end
+```
+
+Poll messages don't say which scanner they came from, so give each scanner its own process,
+and poll again only after the previous result arrives.
+
+Partitioned log tables can't be scanned yet.
diff --git a/fluss-rust/bindings/elixir/lib/fluss/admin.ex b/fluss-rust/bindings/elixir/lib/fluss/admin.ex
index 11bc2ce8e6..82d6276c9e 100644
--- a/fluss-rust/bindings/elixir/lib/fluss/admin.ex
+++ b/fluss-rust/bindings/elixir/lib/fluss/admin.ex
@@ -19,6 +19,10 @@ defmodule Fluss.Admin do
@moduledoc """
Admin client for DDL operations (create/drop databases and tables).
+ `create_*` and `drop_*` default to ignoring an existing or missing database or table, and
+ return `:ok`; pass `false` as the last argument to get an error instead. `drop_database/3`
+ doesn't cascade: drop the tables first.
+
## Examples
admin = Fluss.Admin.new!(conn)
diff --git a/fluss-rust/bindings/elixir/mix.exs b/fluss-rust/bindings/elixir/mix.exs
index cf22f29156..03f9361baf 100644
--- a/fluss-rust/bindings/elixir/mix.exs
+++ b/fluss-rust/bindings/elixir/mix.exs
@@ -19,6 +19,7 @@ defmodule Fluss.MixProject do
use Mix.Project
@version "1.0.0"
+ @source_url "https://github.com/apache/fluss"
def project do
[
@@ -30,6 +31,10 @@ defmodule Fluss.MixProject do
aliases: aliases(),
deps: deps(),
description: "Elixir client for Apache Fluss",
+ name: "Fluss",
+ source_url: @source_url,
+ homepage_url: "https://fluss.apache.org",
+ docs: docs(),
dialyzer: [
plt_add_apps: [:ex_unit, :mix],
plt_file: {:no_warn, "priv/plts/dialyzer.plt"},
@@ -72,6 +77,30 @@ defmodule Fluss.MixProject do
]
end
+ defp docs do
+ [
+ main: "readme",
+ extras: ["README.md", "guides/log_tables.md", "guides/data_types.md"],
+ groups_for_extras: [Guides: ~r{guides/}],
+ # The binding lives in a monorepo subdirectory.
+ source_url_pattern: "#{@source_url}/blob/main/fluss-rust/bindings/elixir/%{path}#L%{line}",
+ groups_for_modules: [
+ "Connection & Configuration": [Fluss, Fluss.Config, Fluss.Connection, Fluss.Error],
+ Admin: [
+ Fluss.Admin,
+ Fluss.DatabaseDescriptor,
+ Fluss.DatabaseInfo,
+ Fluss.Schema,
+ Fluss.SchemaInfo,
+ Fluss.ServerNode,
+ Fluss.TableDescriptor,
+ Fluss.TableInfo
+ ],
+ "Log Tables": [Fluss.Table, Fluss.AppendWriter, Fluss.WriteHandle, Fluss.LogScanner]
+ ]
+ ]
+ end
+
defp package do
[
licenses: ["Apache-2.0"],