Engineering Note
Apache Fluss 1.0: What's Changed for Queries and Go Applications?
Connect Fluss 1.0 batch reads, Spark Union Read, retention, and operational improvements to earlier examples, with fluss-go 0.2.0-beta.1 compatibility and migration guidance.
Apache Fluss 1.0 was announced on September 21, 2026, its first major release after graduation to an Apache Top-Level Project. The separate Go client fluss-go followed with v0.2.0-beta.1 for Fluss 1.0.
Fluss is a storage system for real-time reads and writes that connects recent data with lake history. A Primary Key Table (PK table) can hold current customer state; a Log Table can hold an append-only history of orders.
Earlier posts explored these two models. This article focuses on what 1.0 changes for small state-table queries, Spark analysis over recent and historical data, and cluster operations. A Go example then demonstrates server-side scanning.
This article uses the release and 1.0 source documentation available on September 22, 2026. Earlier exercises target 0.9.1. The examples below require a separate 1.0 environment, including a matching Flink connector.
Changes worth evaluating
| Task | Capability to examine | Scope to check |
|---|---|---|
| Inspect a small customer table | Server-side PK scan | Server-held scan sessions |
| Analyze recent and historical orders | Spark batch Union Read | Lake integration and tiering |
| Retain state and history appropriately | PK row TTL and local log TTL | Separate retention domains |
| Maintain a cluster | Coordinator HA and resource protection | Recovery and retry behavior |
| Connect a Go service | fluss-go 0.2 | Incompatible with the 0.9.1 protocol |
A point lookup, a small full-table scan, and historical analysis need different read paths. Matching the path to the task matters more than counting new APIs.
Inspecting current state
The previous Quickstart used counts, limited previews, and key lookups. Version 1.0 broadens Flink batch reads and adds a server-side PK scan. For a small current-state table, the benefit is straightforward: the client no longer needs to download snapshots and replay change logs. The default strategy in 1.0, snapshot-merge, still combines snapshots with subsequent changes. The optional server-scan reads key-value (KV) state at the TabletServer. This is an alternative for tasks such as refreshing a small administration view, not a replacement for single-key lookups.
For a small demo.customer_profile table without a lake snapshot, select the strategy for one query in a Fluss 1.0 Flink Catalog:
SET 'execution.runtime-mode' = 'batch';
SELECT *
FROM demo.customer_profile
/*+ OPTIONS('client.scanner.kv.batch-strategy' = 'server-scan') */;
This returns current customer rows, not their entire change history. The exercise below creates the same schema in a separate database and verifies this read strategy.
Server-side scans suit small interactive reads. Each bucket opens its own scanner, so this is not a single transaction snapshot across the whole table. Sessions consume server resources. A PK table with a lake snapshot uses Union Read instead, and this option does not change that path. 1.0 Flink reads
Log reads also gain predicate pushdown: the server can skip Arrow log batches whose column statistics rule out a match. For an order-amount filter, this can reduce network transfer and client deserialization. Only batches written after statistics are enabled benefit, and remaining batches still need row-level filtering. This is separate from the Go scan example and is not supported by this fluss-go beta. Filter pushdown
Spark reads recent data alongside lake history
Spark batch Union Read extends the combined view to Spark analysis. With a correctly configured lake-enabled table, a query can include recent Fluss data that has not yet reached lake files.
That distinction matters when comparing a Fluss-table query with a query against lake files alone. Union Read does not make those files immediately current; tiering still determines when they are updated. Union Read documentation
Hudi integration expands the choice of lake formats. Paimon historical partition access also allows supported tables to handle lookups and corrections after a partition has left Fluss hot storage. Think of a late correction to an older order. This has specific eligibility requirements, including a single partition column on an auto-partitioned Paimon table; it is not a universal capability of every lake format. 1.0 partitioning
Retention follows the role of the data
Temporary customer state, change history, and locally retained logs need different lifetimes. Time To Live (TTL) defines how long data is retained before expiry. New PK row TTL and local log TTL settings let these lifetimes be configured separately.
| Setting or policy | Data it manages |
|---|---|
table.kv.ttl | Primary-key rows |
table.log.ttl | Log retention |
table.log.local-ttl | Locally retained logs |
| Partition retention | Whole old partitions |
| Lake retention | Tiered lake data |
Expired PK rows are cleaned during compaction. TTL does not promise deletion at an exact instant across every storage layer. A longer row TTL does not preserve a partition that expires under its partition policy. Begin with the question of which data must remain readable in each layer. 1.0 TTL documentation
Keeping a cluster running
A successful query does not establish operational readiness. Version 1.0 also adds tools for recovery and resource management.
| Situation | Capability | What to check |
|---|---|---|
| Coordinator failure or restart | Automatic failover to a standby | Administration requests may fail briefly during election |
| Expanding local storage | Multiple disks through data.dirs | Preserve existing data directories when changing configuration |
| Disk shortage or KV write pressure | Throttling or rejecting writes to protect resources | Application retries and capacity alerts are still needed |
Standby Coordinators automate failover, but do not guarantee uninterrupted administration requests. Resource protection also does not create extra capacity: operators still need to identify why requests slow down or fail and decide when to retry or expand storage. Operational highlights, Upgrade configuration notes
Konduo is an operations platform connecting infrastructure status, metric and log evidence, alerts, and operating history. It uses fluss-go v0.2.0-beta.1 to provide management, operations, and monitoring for Apache Fluss 1.0 clusters. The client’s 1.0 support therefore serves not only example applications but also tooling for running actual clusters.
More ways for applications to connect
The Gateway provides HTTP access for metadata, table management, and batch writes. Rust, Python, and C++ clients are now maintained in the main repository and released alongside Fluss, aligning protocol and feature changes for users of those languages. These paths help applications use continuously updated profiles and features, which explains the release announcement’s AI emphasis.
MCP, PGSQL, and gRPC are future Gateway plans in the announcement, not shipped interfaces to assume in an integration. Fluss 1.0 announcement
Using server-side scans from Go
Pletor’s fluss-go is a separate client, not an Apache official SDK. It remains pure Go rather than a wrapper around the Rust core.
| Fluss cluster | fluss-go |
|---|---|
0.9.1-incubating | v0.1.0-beta.11 |
1.0.0 | v0.2.0-beta.1 |
Earlier blog examples pin beta.10. Beta.11 is the maintenance version named by the new migration guide for 0.9.1 users. Changing the dependency to 0.2 while retaining a 0.9.1 server is not supported. Migration guide
Print current customer rows as JSON
Use the previous Quickstart’s customer schema in a separate database, release_1_0_demo.customer_profile, so preparation does not overwrite existing Quickstart data. The Go program only reads this non-partitioned PK table.
You need Docker and Compose. Go also runs in a container below, so no host Go installation is required. Validation used four CPUs and 8GB of memory allocated to Docker.
Download these three files into a new directory:
- Test environment: compose.yaml
- Create, seed, and query the table: prepare.sql
- Complete Go example: scan-profiles.go
The environment pins Fluss 1.0.0 and Flink Quickstart 1.20-1.0.0. It tests server-side scanning without lake integration and uses a shared local volume instead of remote object storage. This is a single-machine exercise, not a production deployment.
docker compose up -d --wait --wait-timeout 180 &&
docker compose run --rm sql-client -f /examples/prepare.sql
--wait checks Fluss RPC ports, the Flink REST response, and TaskManager slot registration. SQL runs after && only if these checks pass. If readiness is not reached within 180 seconds, the command fails without running SQL. These checks establish prerequisites for the exercise, not comprehensive cluster health.
On failure, inspect docker compose ps and docker compose logs coordinator-server tablet-server jobmanager taskmanager, address the cause, and rerun the command. Use Docker Compose v2 with support for --wait.
The preparation script inserts these three customers and reads them with a server-side scan. Table creation deliberately fails if the table already exists, preventing repeated preparation from overwriting unknown data.
| customer_id | name | membership | account_balance |
|---|---|---|---|
| 101 | Mina | STANDARD | 125.50 |
| 102 | Joon | VIP | 240.00 |
| 103 | Sara | STANDARD | 75.25 |
Run the Go example inside the same Compose network to avoid host-name mapping. Run this command from the download directory; the first run also downloads Go dependencies.
docker run --rm --network fluss-release-demo_default \
-v "$PWD:/work" -w /work \
-e FLUSS_BOOTSTRAP=coordinator-server:9123 \
golang:1.25.7 sh -c '
test -f go.mod || go mod init example.com/fluss-scan
go get github.com/pletorco/fluss-go@v0.2.0-beta.1 &&
go mod tidy &&
go run scan-profiles.go
'
Running directly on the host
For direct host execution, use Go 1.25.0 or later. The library declares go 1.25.0 and a development toolchain of go1.26.6. A dependency’s toolchain directive does not pin the application’s Go toolchain.
To run directly on the host, prepare the module with the same commands and use FLUSS_BOOTSTRAP=<address> go run scan-profiles.go. The supplied Compose file does not expose host ports. A host-accessible deployment must make both the Coordinator and the advertised TabletServer addresses reachable.
The full program opens a scan session for each bucket:
scanner, err := client.NewBatchScanner(
ctx, table, bucket, fgo.WithBatchSizeBytes(64*1024),
)
if err != nil {
return 0, err
}
defer scanner.Close()
It calls Poll(ctx) until Done, rather than stopping at an empty response, and prints rows from the final response too. It maps column names to values and encodes JSON before releasing response memory with Release(). Decimal values are displayed as strings to preserve precision.
One row has the following shape. This illustrates formatting, not a guaranteed row order or response size.
{
"account_balance": "125.50",
"customer_id": 101,
"membership": "STANDARD",
"name": "Mina"
}
On a fresh table with no concurrent writes, verify all three customers against the seed data and check the final Completed: 3 rows message. Output is not sorted by primary key. Zero rows is normal for an empty table. This exercise does not establish performance or a transaction snapshot across buckets.
On September 22, 2026, we ran table creation, three-row insertion, Flink server-side scanning, and Go JSON reads with this Compose configuration and the Go 1.25.7 container. The three customer values and final row count matched. Spark/lake integration, failover, and performance are outside this exercise’s verified scope.
Testing the JSON formatter
The formatting test covers decimal amounts, NULL, empty results, and row-width mismatches; download it alongside the example and run go test ./....
Run docker compose down after the exercise. This configuration is not a persistent deployment designed to recover tables after recreation. To remove test data as well, run docker compose down --volumes only from this exercise directory. To rerun preparation in a running environment, first confirm the table contains only exercise data and explicitly delete it.
Keep the client boundary visible
This beta adds scan sessions and administration capabilities, but does not support Arrow predicate pushdown or historical physical-partition routing for lake workflows. A server feature is not automatically a Go-client feature. Release support boundaries
Upgrade order depends on the component
Apache’s general sequence is clients and connectors, then tiering, then the cluster. Lake-reader compatibility with newly created lake schemas is one reason for this order. In contrast, fluss-go 0.2 cannot connect to 0.9.1: deploy that Go application after its 1.0 server environment is ready.
Do not apply a universal “server first” rule. Check:
- Connector, lake plugin, and tiering versions and their upgrade order.
- When Go traffic moves to 1.0 and which environment supports rollback.
- Matching versions for the root Go module and optional adapters.
DESCRIBEpermissions if an existing table appears missing.- Whether older readers can consume newly written formats and lake schemas.
Enabling table.statistics.columns writes extended V1 Arrow log batches. Upgrade affected readers and servers before enabling it. Disabling the setting does not rewrite existing batches to the old format. Apache upgrade notes
A practical starting point
Start with a server-side scan of a small PK table. Then evaluate the Union Read or retention behavior your application needs. Validate Go connections, reads, and cleanup in a separate 1.0 environment before moving traffic.
The useful question is how much simpler your own current-state access, historical analysis, and operations become with this release.
Further Reading
- What Is Apache Fluss? - How recent data and lake history fit together.
- Apache Fluss First Run - Customer state and order events in the 0.9.1 Quickstart.
- Read and Write Apache Fluss Tables from Go - The 0.1 client architecture and read/write examples.