Skip to content

feat: Implement persistence#14

Open
styczynski wants to merge 70 commits into
Unoperate:mainfrom
pibuxd:styczynski/branch-to-tag-new-version
Open

feat: Implement persistence#14
styczynski wants to merge 70 commits into
Unoperate:mainfrom
pibuxd:styczynski/branch-to-tag-new-version

Conversation

@styczynski

@styczynski styczynski commented Feb 5, 2026

Copy link
Copy Markdown

This change is Reviewable

About

This PR adds persistence support to the Bigtable emulator.

AI summary

Short AI-generated summary of changes based on README:

The main implementation lives in persist/storage.h, which defines an abstract Storage interface and row-transaction model. PersistedTable provides the table interface that can be backed by any implementation of this interface. Two backends are provided: MemoryStorage (persist/memory/)—a thin wrapper around the existing in-memory class hierarchy with no persistence—and RocksDBStorage (persist/rocksdb/), which uses RocksDB TransactionDB for ACID guarantees. Table metadata and configuration use protobufs in persist/proto/storage.proto. The patch avoids large changes to top-level files while preserving existing behavior and enabling other storage backends in the future.

Contributors

  • ⭐⭐ Piotr Styczyński
    • Majority of the code for the storage interface and tests
  • ⭐ Piotr Bublik
    • Implemented all core data operations (MutateRow, ReadRows, CheckAndMutateRow, ReadModifyWriteRow)
    • Solved RocksDB TransactionDB column family creation constraints
    • Implemented server endpoints to use the PersistentTable implementation
  • Mikołaj Woliński
    • Minimal README updates and fixed some TODOs

Changes

  • We avoided modifying the highest-level files too much to prevent PR diff explosion.
  • We implemented an abstract storage interface (and abstract row transaction) in storage.h, which can be used to implement other backends in the future if needed.
  • We implemented a wrapper in persist/memory around the existing in-memory class hierarchy so that all existing functionality is preserved.
  • We added unit tests (for the server/emulator implementation and lower-level storage) and integration tests (queries using the Bigtable client).
  • Some logic was extracted from the highly unmodular code in table.h. One such utility is StorageFilteredTableStream. You can remove FilteredTableStream from table.h if you deem it necessary.

Future work (from README)

The following items are left for future improvement:

  1. Garbage collection — Automatic deletion of old cell versions based on gc_rule. See TODO in code.
  2. Atomic schema updates — ModifyColumnFamilies does not yet persist changes to storage.
  3. Efficient bulk deletes — DropRowRange could use RocksDB DeleteRange instead of iteration.
  4. Better CF management — Drop/Create column families sometimes requires a DB restart; this should be straightforward to improve.

Testing

Tests are provided at several levels:

Test file Level Storage Description
persist/integration/read_test.cc Integration RocksDB Uses the CBT client: creates a table, applies mutations, reads rows, and verifies that data persists across restarts.
persist/memory/storage_test.cc Unit MemoryStorage/RocksDBStorage Exercises the Storage interface: CreateTable, row transactions, SetCell, DeleteRowColumn, DeleteRowFromColumnFamily, etc.
cluster_test.cc Unit RocksDB Cluster API: CreateTable, ListTables, GetTable, mutations, CheckAndMutateRow for a given storage backend.
server_test.cc Unit RocksDB Server RPC layer: gRPC Bigtable and BigtableTableAdmin stubs against an in-process emulator server.

We also tested manually using CBT to create and read tables and confirmed that data persists across emulator restarts.

Data model

storage_diagram_rdb storage_diagram_mem

mik04rm and others added 30 commits October 21, 2025 16:22
- Migrate from in-memory Table to RocksDB-backed Table2
- Implement all core data operations (MutateRow, ReadRows, CheckAndMutateRow, ReadModifyWriteRow)
- Add Storage transaction API with ACID guarantees
- Solve RocksDB TransactionDB column family creation constraints
- Implement dynamic CF management with DB reopen mechanism
- Add protobuf serialization for table metadata
- Update server endpoints to use persistent Table2
- Clean up debug statements and add TODO markers for future work
- Update README with implementation details and completion status
- All 9/9 tests passing (8 legacy + 1 new server_test)
styczynski and others added 24 commits February 4, 2026 04:14
refactor: general code refactor
* Document Bazel compilation cache (#7)

* bazel: configure clang-tidy (Unoperate#8)

* bazel: configure compile_commands.json generator (#6)

* feat: Implement GC for Column Families (#3)

Co-authored-by: Adam Czajkowski <prawilny@unoperate.com>

* docs: unify README style, deduplicate development section (Unoperate#10)

* fix: remove unused import

---------

Co-authored-by: Adam Czajkowski <prawilny@unoperate.com>
Co-authored-by: Brian Gitonga Marete <bgm@unoperate.com>
@styczynski

styczynski commented Feb 5, 2026

Copy link
Copy Markdown
Author

Recent changes:

  • Updated PR description to provide storage model diagrams (updated the diagram as previous refferred to old model)
  • Minor README updates

Should be ready for review.

@styczynski styczynski left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes: Left self-review comments with questions for reviewers and clarifications about implementation choices.

Comment thread .vscode/tasks.json
@@ -0,0 +1,48 @@
{

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review: I think we should leave this file as it's useful for local development.
Querstion to reviewer: how to proceed?

Comment thread column_family.h
#include <google/bigtable/v2/data.pb.h>
#include <google/protobuf/duration.pb.h>
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/stubs/mutex.h>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review: Is this required? This header seems to be unused in this context and failed to build on my machine.
Querstion to reviewer: Is it ok?

Comment thread README.md
This means that we need to do some suboptimal stuff in `storage.h`. To stream rows we need to construct iterator. As we group the data we load one row at a time - see `RocksDBStorageRowTX::LoadRow()`. When it happens we
need to map protobuf into C++ map to get correct ordering. I think this is avoidable somehow, but that kind of performance issue isn't our highest-priority concern right now.

### CustomStorage: implementing custom storage interface

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review: we can provide no-op minimal Storage interface implementation if it's useful
Querstion to reviewer: do you want us to provide minimal no-op implementation as an example of extension for external user?

Comment thread .bazelrc
# disables C++14 features, even if the compilers defaults to C++ >= 14
build:linux --cxxopt=-std=c++14
build:macos --cxxopt=-std=c++14
build:linux --cxxopt=-std=c++17

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review: Our code uses C++17, so it might be necessary to update comment here / and probably README
Querstion to reviewer: should we update README or anything else? Or not?


/**
* This is very similar to FilteredTableStream.
* We didn't want to move all the files to persist/ directory, because that would make diff

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review: Question for reviewer here how to proceed. This can be done via templatization of original class in table.h.
Querstion to reviewer: Can we proceed and mark it as TODO for someone in the future?

Comment thread persist/persisted_table.h
* (update schema, modify column families, drop row range) against persisted
* storage.
*/
class PersistedTable {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review: This is probably misleading name now. We have MemoryStorage so this documentation is no longer true. PersistedTable is just next version of table interface, more generic successor to table.h that can accept any Storage instance.

Querstion to reviewer: should we rename?

Comment thread persist/metadata_view.h
* Storage::Tables() guarantees:
* - Random const access iterator (specifically std continous range)
* - Provides snapshot of tables when call to Tables() happened, even if mutations happen in meantime
*/

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review: We can probably just remove it entirely and use vector<tuple> directly. It was previosuly used as a utility to hold abstract iterator for abstract Storage (to simplify polymorphism and not use templated Storage class). After refinement I came to conclusion that this interface became thin wrapper around vector.

What probably should be done is:

  1. Remove this file entirely
  2. Return vector directly in Tables() method
  3. We can add method TablesNames() to storage that will extract names via transform

Question to reviewer: how to proceed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants