# About

Welcome to Speedb

<figure><img src="/files/8zAcdxHNJVWBE3MjJDSu" alt=""><figcaption></figcaption></figure>

## Welcome to Speedb's documentation!

Speedb is a first-of-its-kind, community-led storage engine designed to support modern data sets. It focuses on high performance, optimized for modern storage hardware and scale, on-premise and in the cloud. We strive to simplify the usability of complex data engines as well as stabilize and improve performance.\
\
Speedb docs is managed in the "[documentation](https://github.com/speedb-io/documentation)" repository.

Contributions are not just about code. Help the community learn more about Speedb and RocksDB by adding to our Docs.&#x20;

You can contribute by enhancing, fixing issues, and adding new content, by using issues, and pull requests.


# Speedb Use Cases

Speedb common use cases

Speedb is an embedded key-value store that stores data as key-value pairs and is designed to be embedded in applications. Speedb is commonly used in a variety of applications where low latency and high performance are important, such as in web browsers, streaming applications and mobile apps.

Speedb is primarily used for the following use cases:<br>

1. **Streaming and microprocessing:** increasing application performance such as streaming applications, microprocessing and more. Streaming applications like Apache Flink and Kafka Streams are using Speedb to store states in a fast and efficient manner.&#x20;
2. **Storage engine:** A storage engine usually handles basic operations of storage management, most notably to create, read, update, and delete (CRUD) data. In addition, the data engine needs to efficiently provide an interface for sequential read of data and atomic update of several keys at the same time. Replacing the existing storage engine of a database with Speedb will boost the performance and ensure consistent performance over time.&#x20;
3. **Real-time data processing by IOT**: Internet of Things devices oftenly collect and generate large amounts of data, which needs to be stored for future use. Also it sometimes needs processing power to perform complex data processing tasks. Speedb is used as a high performance storage engine to perform these tasks and store the large amount of data that is needed.&#x20;
4. **Storage Metadata**: As the amount of data and number of files (and object) is increasing dramatically over time, the amount of metadata is growing out of control.\
   Since metadata needs to be accessed fast, on a large scale, and needs to be consistent, an embedded KVS is the preferred method of metadata management from most storage systems today.
5. **Gaming:** store game state data, allowing for fast access and updates to game data such as state of a game, including player progress, game statistics, user profile and preferences. The fast access and the high performance of Speedb makes it well suited in games that need to store and retrieve large amounts of data quickly.&#x20;
6. **Mobile apps:** store data locally on mobile devices, allowing for offline access and faster data retrieval. Using Speedb on mobile applications can help to improve the performance, user experience and overall functionality of the application.&#x20;
7. **Caching**: Speedb can be used as a cache to store frequently accessed data, allowing for faster access and reduced load on the underlying database.
8. **Metrics and analytics:** Speedb can  be used to store and retrieve metrics and analytics data, such as application performance data or user behavior data. This can help to provide valuable insights into the usage of the app and inform decisions about future development.

\
\
\
\ <br>


# Speedb Communication Channels

###

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Speedb official website</strong></td><td></td><td></td><td><a href="/files/JyL1TYTxTjv0aSiA9yI1">/files/JyL1TYTxTjv0aSiA9yI1</a></td><td><a href="https://www.speedb.io">https://www.speedb.io</a></td></tr><tr><td><strong>Discord server</strong></td><td>News, live discussions and community support</td><td></td><td><a href="/files/cPqiFdp9wtftadHp3GBQ">/files/cPqiFdp9wtftadHp3GBQ</a></td><td><a href="https://discord.gg/5fVUUtM2cG">https://discord.gg/5fVUUtM2cG</a></td></tr><tr><td><strong>Reporting issues</strong></td><td>You can report a bug in Speedb by opening a Github issue in the Speedb repository on GitHub</td><td></td><td><a href="/files/Bbuw8eF6ooCY0WXgvqhx">/files/Bbuw8eF6ooCY0WXgvqhx</a></td><td><a href="https://github.com/speedb-io/speedb/issues">https://github.com/speedb-io/speedb/issues</a></td></tr></tbody></table>

<br>


# Release Cadence

Speedb releases follow the usual major.minor.patch semantic versioning schema.&#x20;

**Major versions**

Major versions introduce new capabilities and significant changes. It's possible that these versions don't maintain backward compatibility.&#x20;

{% hint style="info" %}
Upgrading to a new major version may require some code changes.&#x20;
{% endhint %}

#### Minor versions

Minor versions usually contain extended functionality, without breaking compatibility.&#x20;

{% hint style="info" %}
Upgrade to a new minor version is expected to be seamless&#x20;
{% endhint %}

#### Patches

Patch-level versions consist primarily of backward-compatible bug fixes.&#x20;

Default values may also be changed with patch versions, as long as they don't impact the system.&#x20;

{% hint style="info" %}
Upgrading from the previous patch version is expected to be seamless.
{% endhint %}

### Release schedule

Our goal is to release a new version approximately every two months.

* The version number will be based on the content and semantic versioning
* Any critical bug fixes will be resolved outside of the release cycle.
* We will rebase to a newer Rocksdb version occasionally, changing the release number accordingly


# Releases

This page summarize information about the Speedb releases.

Find out what's new in Speedb!

This page summarize information about new features, bugs fixes and enhancements per release.

<details>

<summary><a href="https://github.com/speedb-io/speedb/tree/speedb/v2.8.0">v2.8.0</a></summary>

## Speedb 2.8.0 (31-January-2024)

**The following release is based on RocksDB 8.6.7**

### New Features

* Memory Tracking: provides insights about the memory consumption per component. This works in debug mode only.  by [@ofriedma](https://github.com/ofriedma) in [#757](https://github.com/speedb-io/speedb/pull/757)

### Enhancements

* Add JAVA and C Interface for EnableSpeedbFeatures by [@udi-speedb](https://github.com/udi-speedb) in [#772](https://github.com/speedb-io/speedb/pull/772)
* LOG Enhancement:Have a separate LOG entry per CF Stats by [@udi-speedb](https://github.com/udi-speedb) in [#801](https://github.com/speedb-io/speedb/pull/801)
* Export GetFlushReasonString/GetCompactionReasonString in listener.h by [@git-hulk](https://github.com/git-hulk) in [#785](https://github.com/speedb-io/speedb/pull/785)

### Bug Fixes

* stress test: Fixed TestIterateAgainstExpected not supporting 0 iterations by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#796](https://github.com/speedb-io/speedb/pull/796)
* Fixed conflicts between db\_bench flags and enable speedb features flag by [@ofriedma](https://github.com/ofriedma) in [#765](https://github.com/speedb-io/speedb/pull/765)
* stress test: Disable IsDone assertion which fails with tracing sometimes by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#816](https://github.com/speedb-io/speedb/pull/816)
* Refresh exist iterator isnot supported in hash spdb and vector memtablerep should avoid that ([#802](https://github.com/speedb-io/speedb/issues/802)) by [@ayulas](https://github.com/ayulas) in [#811](https://github.com/speedb-io/speedb/pull/811)
* 812 stress test error initiateflushesthread assertion fail num running flushes 0 by [@udi-speedb](https://github.com/udi-speedb) in [#817](https://github.com/speedb-io/speedb/pull/817)

### Miscellaneous

* Options: Set compaction\_readahead\_size default to 0 by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#792](https://github.com/speedb-io/speedb/pull/792)
* Options: Set level\_compaction\_dynamic\_level\_bytes as false by default by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#791](https://github.com/speedb-io/speedb/pull/791)
* stress test: Add the ability to trace all operations by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#797](https://github.com/speedb-io/speedb/pull/797)
* zlib: Update ci and Makefile using a different link by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#820](https://github.com/speedb-io/speedb/pull/820)
* Remove some db stress non existence parameters by [@ofriedma](https://github.com/ofriedma) in [#814](https://github.com/speedb-io/speedb/pull/814)
* Revert "added exit 0 to the license check" by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#790](https://github.com/speedb-io/speedb/pull/790)
* Compaction: Restore SetupForCompaction functionality by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#788](https://github.com/speedb-io/speedb/pull/788)
* Add more checks for db\_stress enable speedb features by [@ofriedma](https://github.com/ofriedma) in [#803](https://github.com/speedb-io/speedb/pull/803)

### Known Issue

* With this release, user-defined timestamps are not supported.

**Full Changelog**: <https://github.com/speedb-io/speedb/commits/speedb/v2.8.0>

</details>

<details>

<summary>v2.7.0</summary>

Release date: 25-October-2023

**The following release is based on RocksDB 8.1.1**

### New Features

* Support Non-Blocking Manual Compactions (CompactRange) ([#597](https://github.com/speedb-io/speedb/issues/597)) by [@udi-speedb](https://github.com/udi-speedb) in [#656](https://github.com/speedb-io/speedb/pull/656)

### Enhancements

* Logger: Add reporting capabilities to the WBM by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#556](https://github.com/speedb-io/speedb/pull/556)
* Report static pinning options to the log ([#691](https://github.com/speedb-io/speedb/issues/691)) by [@udi-speedb](https://github.com/udi-speedb) in [#707](https://github.com/speedb-io/speedb/pull/707)
* Add the offset and size to the dump SST footer details by [@git-hulk](https://github.com/git-hulk) in [#679](https://github.com/speedb-io/speedb/pull/679)
* 653: Support hash spdb as part of enable Speedb features by [@ayulas](https://github.com/ayulas) in [#655](https://github.com/speedb-io/speedb/pull/655)
* Enable-Speedb-Feature: Use Scoped Pinning Policy instead of Default Policy by [@udi-speedb](https://github.com/udi-speedb) in [#715](https://github.com/speedb-io/speedb/pull/715)\
  **Note: We recommend only using Enable-Speedb-feature in POC with this release, since the configuration set by Enable-Speedb-feature might not be saved after recovery.**
* Make a static pinning decision based on the last level with data (not bottommost level) ([#626](https://github.com/speedb-io/speedb/pull/626)) by [@udi-speedb](https://github.com/udi-speedb) in [#684](https://github.com/speedb-io/speedb/pull/684)
* db bench: usability of the pinning policy parameter by [@udi-speedb](https://github.com/udi-speedb) in [#720](https://github.com/speedb-io/speedb/pull/720)
* Static Pinning: Set the default for last-level-with-data to 70 by [@udi-speedb](https://github.com/udi-speedb) in [#727](https://github.com/speedb-io/speedb/pull/727)

### Bug Fixes

* Fixed repeatable threads not working with on thread start feature by [@ofriedma](https://github.com/ofriedma) in [#667](https://github.com/speedb-io/speedb/pull/667)
* Fixed a JAVA build issue introduced by [#597](https://github.com/speedb-io/speedb/issues/597) ([#680](https://github.com/speedb-io/speedb/issues/680)) by [@udi-speedb](https://github.com/udi-speedb) in [#681](https://github.com/speedb-io/speedb/pull/681)
* db\_bench: Fixed a conflict between the enable\_speedb\_features and pinning\_policy flags by [@udi-speedb](https://github.com/udi-speedb) in [#730](https://github.com/speedb-io/speedb/pull/730)

### Miscellaneous

* db\_bench: fix SeekRandomWriteRandom valid check by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#652](https://github.com/speedb-io/speedb/pull/652)
* Fixed unused variable warnings in db\_bench\_tool.cc by [@git-hulk](https://github.com/git-hulk) in [#671](https://github.com/speedb-io/speedb/pull/671)
* Add MacOS support for thread affinity tests by [@ofriedma](https://github.com/ofriedma) in [#661](https://github.com/speedb-io/speedb/pull/661)
* Fixed some build issues by [@mrambacher](https://github.com/mrambacher) in [#664](https://github.com/speedb-io/speedb/pull/664)
* Fixed build errors by [@ofriedma](https://github.com/ofriedma) in [#717](https://github.com/speedb-io/speedb/pull/717)
* Fixed Java Samples by [@mrambacher](https://github.com/mrambacher) in [#721](https://github.com/speedb-io/speedb/pull/721)
* Add support for ASAN builds on Mac by [@mrambacher](https://github.com/mrambacher) in [#608](https://github.com/speedb-io/speedb/pull/608).
* Fixed a compilation error in scoped\_pinning\_policy.cc by [@udi-speedb](https://github.com/udi-speedb) in [#725](https://github.com/speedb-io/speedb/pull/725)
* Change Configurable::SerializeOptions method to return a map by [@mrambacher](https://github.com/mrambacher) in [#619](https://github.com/speedb-io/speedb/pull/619)

### Thanks to the new contributor! 💙

* [@git-hulk](https://github.com/git-hulk) made their first contribution in [#671](https://github.com/speedb-io/speedb/pull/671)

</details>

<details>

<summary>v2.6.0</summary>

Release date: 22-August-2023 | Based on RocksDB 8.1.1

### New Features

* Introduce a TablePinningPolicy to control how and when memory is pinned by [@mrambacher](https://github.com/mrambacher) in [#459](https://github.com/speedb-io/speedb/pull/459)
* Snapshot Optimization ([#35](https://github.com/speedb-io/speedb/issues/35)) by [@ofriedma](https://github.com/ofriedma) in [#547](https://github.com/speedb-io/speedb/pull/547)
* Added Speedb tuning function that optimizes configuration for single and multi databases by [@RoyBenMoshe](https://github.com/RoyBenMoshe) in [#543](https://github.com/speedb-io/speedb/pull/543)
* Support Speedb sorted hash memtable in Java and C by [@ayulas](https://github.com/ayulas) in [#612](https://github.com/speedb-io/speedb/pull/612)
* Add on thread start callback by [@ofriedma](https://github.com/ofriedma) in [#629](https://github.com/speedb-io/speedb/pull/629)

### Miscellaneous

* Redo of Index/Filter/Data blocks sizes in Block (LRU) Block Cache per CF after rebase on RocksDB 8.1 ([#516](https://github.com/speedb-io/speedb/issues/516)) by [@udi-speedb](https://github.com/udi-speedb) in [#620](https://github.com/speedb-io/speedb/pull/620)
* Move to zlib 1.3 (Makefile) by [@ayulas](https://github.com/ayulas) in [#646](https://github.com/speedb-io/speedb/pull/646)
* 639: move hashSpdb memtable from plugin to main code by [@ayulas](https://github.com/ayulas) in [#640](https://github.com/speedb-io/speedb/pull/640)

### Bug Fixes

* WriteController: move it to public interface by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#579](https://github.com/speedb-io/speedb/pull/579)
* Fixed log trace of Block Cache (Entry) Stats dump ([#577](https://github.com/speedb-io/speedb/issues/577)) by [@udi-speedb](https://github.com/udi-speedb) in [#578](https://github.com/speedb-io/speedb/pull/578)
* Added consideration to flushes that were not initiated by the write buffer manager by [@udi-speedb](https://github.com/udi-speedb) in [#632](https://github.com/speedb-io/speedb/pull/632)
* 606 stuck switch with plug by [@ayulas](https://github.com/ayulas) in [#610](https://github.com/speedb-io/speedb/pull/610)
* 570 spdb memtable use seek paralel threshold mishandled by [@ayulas](https://github.com/ayulas) in [#603](https://github.com/speedb-io/speedb/pull/603)

**Full Changelog**: [speedb/v2.5.0...speedb/v2.6.0](https://github.com/speedb-io/speedb/compare/speedb/v2.5.0...speedb/v2.6.0)

</details>

<details>

<summary>v2.5.0</summary>

Release date: 14-June-2023 | Based on RocksDB 8.1.1

### New Features

* Dirty memory: connect wbm to global delay : Delay writes gradually based on memory usage of the WriteBufferManager (WBM) in order to gain stability. To use this feature, pass allow\_stall = true to the ctor of WBM and the db needs to be opened with options.use\_dynamic\_delay = true. by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#423](https://github.com/speedb-io/speedb/pull/423)
* [Prevent flush entry followed delete operations currently during memtable flush](/enhancements/remove-single-delete-elements-during-memtable-flush) , if the key has a match key in the delete range table and this record has no snapshot related to it, we still write it with its value to the SST file. This feature keeps only the delete record and reduces SST size for later compaction. by [@ayulas](https://github.com/ayulas) in [#418](https://github.com/speedb-io/speedb/pull/418) ([#411](https://github.com/speedb-io/speedb/issues/411))

### Enhancements

* Log: Add the CF name and job ID to all compaction job traces by [@udi-speedb](https://github.com/udi-speedb) in [#511](https://github.com/speedb-io/speedb/pull/511)
* Log: Display cf names in rolled logs with their options by [@udi-speedb](https://github.com/udi-speedb) in [#518](https://github.com/speedb-io/speedb/pull/518)
* Log: Report the name of cf-s whose options are skipped in the log by [@udi-speedb](https://github.com/udi-speedb) in [#520](https://github.com/speedb-io/speedb/issues/520)
* db\_stress: Add cost\_write\_buffer\_to\_cache flag by [@udi-speedb](https://github.com/udi-speedb) in [#513](https://github.com/speedb-io/speedb/pull/513)

### Bug Fixes

* Fixed sorted hash memtable use after free bug by [@ayulas](https://github.com/ayulas) in [#553](https://github.com/speedb-io/speedb/pull/553) [#501](https://github.com/speedb-io/speedb/issues/501)
* Sanitize max\_num\_parallel\_flushes in WBM if 0 by [@udi-speedb](https://github.com/udi-speedb) in [#515](https://github.com/speedb-io/speedb/pull/515)
* WriteController: fix for stop while shutting down. Also switch to waiting a sec on the CV each time. This is required since a bg error \* doesn't signal the CV in the WriteController. by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#499](https://github.com/speedb-io/speedb/pull/499)
* Fixed UnlockWALStallCleared test in utilities/transactions/transaction\_test.cc by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#514](https://github.com/speedb-io/speedb/pull/514)
* Always assume optimize\_filters\_for\_memory=false when creating a paired bloom filter by [@udi-speedb](https://github.com/udi-speedb) in [#528](https://github.com/speedb-io/speedb/pull/528)
* db\_bench and stress: fixed WBM initiation by [@udi-speedb](https://github.com/udi-speedb) in [#510](https://github.com/speedb-io/speedb/pull/510)
* db\_bench: Create a WBM once for all db-s regardless of their use in different groups by [@udi-speedb](https://github.com/udi-speedb) in [#551](https://github.com/speedb-io/speedb/pull/551)
* Fixed Tombstone test failure as a result of not clear local variable by [@ayulas](https://github.com/ayulas) in [#561](https://github.com/speedb-io/speedb/pull/561)
* Makefile: Remove pycache artifacts after running gtest-parallel [#495](https://github.com/speedb-io/speedb/pull/495)
* AVX512: fix disabling other optimizations by [@Yuval-Ariel](https://github.com/Yuval-Ariel) in [#489](https://github.com/speedb-io/speedb/pull/489) [#489](https://github.com/speedb-io/speedb/pull/489)

### Miscellaneous

* Print optimize\_filters\_for\_memory option to the log by [@udi-speedb](https://github.com/udi-speedb) in [#537](https://github.com/speedb-io/speedb/pull/537)

**Full Changelog**: <https://github.com/speedb-io/speedb/commits/speedb/v2.5.0>

</details>

<details>

<summary>v2.4.1</summary>

Release date: 19-April-2023 | Based on RocksDB 7.7.8

### Enhancement

* Add the ability to create any Filter Policy in java (including ribbon filter and the Speedb paired bloom filter)  [#387](https://github.com/speedb-io/speedb/pull/387)

### Bug Fix

* Write Flow: Reduce debug log size. Note: the write flow is still experimental in this release [#472](https://github.com/speedb-io/speedb/pull/472)

</details>

<details>

<summary>v2.4.0</summary>

Release date: 5-April-2023 | Based on RocksDB 7.7.8

#### New Features

* [New beezcli: ](/tools/beezcli-tool)Interactive CLI that offers data access and admin commands [#427](https://github.com/speedb-io/speedb/pull/427)
* [Global delayed write rate](/speedb-features/global-delayed-write): manage the delayed write rate across multiple CFs/databases [#392](https://github.com/speedb-io/speedb/pull/392)
* [New write flow](/speedb-features/write-flow): Major improvement of writing while reading. Note: This feature is experimental and it consumes slightly more memory in this release  [#445](https://github.com/speedb-io/speedb/pull/445)

#### Enhancements

* Skip expired object while using DBWithTtl [#403](https://github.com/speedb-io/speedb/pull/403)

#### Bug Fixes

* Dynamic delay writes: fix pending bytes rate calculation  [#451](https://github.com/speedb-io/speedb/pull/451)
* Global delay write: check again credits under mutex [#438](https://github.com/speedb-io/speedb/pull/438)

#### Miscellaneous

* Add back accidental revert in DropRandomUnsyncedData  [#402](https://github.com/speedb-io/speedb/pull/402)
* Add speedb licenses to code [#409](https://github.com/speedb-io/speedb/pull/409)
* Enforce writing licenses inside a source file [#410](https://github.com/speedb-io/speedb/pull/410)
* Makefile: Use speedb libs in `build_size` target  [#399](https://github.com/speedb-io/speedb/pull/399)
* Replace uint with unsinged int (Windows Build Failure)  [#421](https://github.com/speedb-io/speedb/pull/421)
* crashtest: dont reroll skip\_list or HashSpdRepFactory  [#452](https://github.com/speedb-io/speedb/pull/452)
* options: Forward declare WriteBufferManager [#433](https://github.com/speedb-io/speedb/pull/433)

</details>

<details>

<summary>v2.3.0</summary>

Release date: 15-February-2023 | Based on RocksDB 7.7.8

#### New Feature

* [New Live configuration changes: ](/speedb-features/live-configuration-changes)support changing immutable options on the fly [#294](https://github.com/speedb-io/speedb/pull/294)

#### Enhancements

* Improved performance while using the sorted-hash memtable [#299](https://github.com/speedb-io/speedb/pull/299)
* Added prints and query option of Index size per CF - LRU Cache Only [#368](https://github.com/speedb-io/speedb/pull/368)
* Add F\_BARRIERFSYNC for Sync operations on MacOS (addresses the issue raised in [rocksdb#11035](https://github.com/facebook/rocksdb/issues/11035))  [#319](https://github.com/speedb-io/speedb/pull/319)
* Paired-Bloom-Filter: Balancing rounding to batches between the bottom-most level and other levels [#371](https://github.com/speedb-io/speedb/pull/371)
* db\_bench: recreate only specified DBs in a group of benchmarks  [#370](https://github.com/speedb-io/speedb/pull/370)
* &#x20;Use a NoSyncFileSystem to skip Sync/FSync to reduce test times ( based on RocksDB PR [9545](https://github.com/facebook/rocksdb/pull/9545))  [#380](https://github.com/speedb-io/speedb/pull/380)

#### Bug Fixes

* Delayed Writes: fix L0 calc bug [#311](https://github.com/speedb-io/speedb/pull/311)
* util: Fixed compilation failure on Fedora 35 with gcc 11.2.1 and gflag 2.2.2   [#396](https://github.com/speedb-io/speedb/pull/396)
* Fixed compilation failure on windows [#384](https://github.com/speedb-io/speedb/pull/384)
* Fixed compilation issues on Mac by [#393](https://github.com/speedb-io/speedb/pull/393)
* Use the Test Name for the dbname when running unit tests  [#353](https://github.com/speedb-io/speedb/pull/353)

#### Miscellaneous&#x20;

* add Speedb is awesome example to the getting started section  [#382](https://github.com/speedb-io/speedb/pull/382)
* unit tests: fix CompactionServiceTest.RemoteEventListener ([#314](https://github.com/speedb-io/speedb/issues/314)) [#354](https://github.com/speedb-io/speedb/pull/354)
* artifacts check tool - readme file was updated  [#293](https://github.com/speedb-io/speedb/pull/293)
* don't use AVX512 with asan  [#398](https://github.com/speedb-io/speedb/pull/398)

</details>

<details>

<summary>v2.2.1</summary>

Release date: 30-January-2023 | Based on RocksDB 7.7.8

#### Bug Fixes

* Delayed Writes: fixed L0 calculation bug [#311](https://github.com/speedb-io/speedb/pull/311)

#### Miscellaneous

* Added WBM's cache info to the log [#313](https://github.com/speedb-io/speedb/pull/313)
* db\_bench: set db\_bench defaults to Speedb [#322](https://github.com/speedb-io/speedb/pull/322)
* build: remove the dependency on GNU Parallel for running unit tests [#243](https://github.com/speedb-io/speedb/pull/243)

</details>

<details>

<summary>v2.2.0</summary>

Release date: 22-December-2022 | Based on RocksDB 7.7.3

#### New Features

* [Proactive flushes ](/speedb-features/proactive-flushing)for better resources utilization [#185](https://github.com/speedb-io/speedb/pull/185)
* [Dynamic delayed write ](/enhancements/dynamic-delayed-writes)mechanism for consistent performance [#2](https://github.com/speedb-io/speedb/pull/281)

#### Enhancements&#x20;

* Paired block bloom: Removed the bits-per-key limitation for better results  [#163](https://github.com/speedb-io/speedb/pull/163)
* [DB-bench groups support](/tools/db_bench-groups): Allow running multiple benchmark, each with its own configuration [#250](https://github.com/speedb-io/speedb/pull/250)
* db\_bench: Support '--groups' in addition to '-groups'  [#295](https://github.com/speedb-io/speedb/pull/295)
* db\_stress enhancement: Support control over WBM's allow\_stall [#289](https://github.com/speedb-io/speedb/pull/289)
* [Shorten latency while switch](/enhancements/reduce-switch-memtable-latency) generic memtable  [#297](https://github.com/speedb-io/speedb/pull/297)

#### Bug Fixes

* db\_bench: bug fix inserted   [#265](https://github.com/speedb-io/speedb/pull/265)
* db\_bench: ErrorExit from static func bug  [#278](https://github.com/speedb-io/speedb/pull/278)
* Proactive Flushes: compilation warnings fix  [#307](https://github.com/speedb-io/speedb/pull/307)

#### Miscellaneous

* Added info to the log file for artifact testing  [#286](https://github.com/speedb-io/speedb/pull/286)
* Disable LoadCustomizableTest.LoadMemTableRepFactoryTest  [#305](https://github.com/speedb-io/speedb/pull/305)

</details>

<details>

<summary>v2.1.1</summary>

Release date: 15-November-2022 | Based on RocksDB 7.2.2

#### Bug Fixes

* Shorten latency while switch memtable ([#14](https://github.com/speedb-io/speedb/issues/14))
* Fixed a crash that occurred when using the hash memtable. ([#98](https://github.com/speedb-io/speedb/issues/98))
* memtable\_list: avoid rolling back memtable flush on CF drop ([#144](https://github.com/speedb-io/speedb/issues/144))
* crashtest: fix 0 value of data\_block\_hash\_table\_util\_ratio ([#214](https://github.com/speedb-io/speedb/issues/214))
* deletefile\_test: fix breakage caused by the compaction threads change ([#218](https://github.com/speedb-io/speedb/pull/218))
* cmake: clean up on successful runs and randomise test scheduling ([#202](https://github.com/speedb-io/speedb/issues/202))
* build: add a version build-tag for non-release builds ([#156](https://github.com/speedb-io/speedb/issues/156))
* build: support ccache and sccache in the Makefile build ([#170](https://github.com/speedb-io/speedb/issues/170))
* Update README.md
* docs: fix instructions for building Speedb in README.md and INSTALL.md
* readme typo fix by [@azmisaquib](https://github.com/azmisaquib) ([#223](https://github.com/speedb-io/speedb/pull/223))
* build\_version: apply the build tag to the Speedb version string ([#231](https://github.com/speedb-io/speedb/issues/231))
* build: correctly handle merge commits when calculating a build tag ([#207](https://github.com/speedb-io/speedb/pull/207))
* db\_test2: fix BackgroundPurgeTest ([#236](https://github.com/speedb-io/speedb/issues/236))
* Update HISTORY.md ([#239](https://github.com/speedb-io/speedb/pull/239))
* db\_bench: Fix a bug when destructing a Benchmark with multiple db-s ([#234](https://github.com/speedb-io/speedb/issues/234))
* db\_bench: add benchmark - seektodeletedranges ([#201](https://github.com/speedb-io/speedb/pull/201))

</details>

<details>

<summary>v2.1.0</summary>

Release date: 26-October-2022 | Based on RocksDB 7.2.2

#### New Features

* Added new [Paired bloom filter](/speedb-features/paired-bloom-filter) that reduces false positive rate with the same performance and memory. In some configurations, the memory consumption is even reduced by up to 30%.\
  Note: Paired bloom filter is recommended to use when the number of bits per key is larger than 10. ([#54](https://github.com/speedb-io/speedb/pull/54))
* Added Plugin Tests to builds ([#143](https://github.com/speedb-io/speedb/pull/143))

#### Enhancements

* The default value for the number of compaction threads has changed to 8 ([#194](https://github.com/speedb-io/speedb/pull/194))
* An infrastructure addition for a future feature: added API to retrieve the amount of immutable memory that can be freed. ([#113](https://github.com/speedb-io/speedb/issues/113))
* cmake: allow running the tests in parallel like in the Makefile ([#103](https://github.com/speedb-io/speedb/pull/103))
* build: fix the java test target dependencies ([#129](https://github.com/speedb-io/speedb/pull/129))
* flush\_job: do not roll back memtable flush on CF drop and DB shutdown ([#127](https://github.com/speedb-io/speedb/pull/127))
* When background purges are used, set their priority to low instead of high, ([#151](https://github.com/speedb-io/speedb/pull/151))
* Added db\_bench option to change the parameter: avoid\_unnecessary\_blocking\_io ([#184](https://github.com/speedb-io/speedb/pull/184))
* Allow construction of Filter Policy from uri to the tools ([#83](https://github.com/speedb-io/speedb/pull/83))

#### Miscellaneous

* Remove the GPL as an alternative license ([#119](https://github.com/speedb-io/speedb/pull/119))
* Fix shell tab-completions in makefile ([#148](https://github.com/speedb-io/speedb/pull/148))
* Added Speedb change-log to the HISTORY.md file ([#189](https://github.com/speedb-io/speedb/pull/189))
* makefile: rework the dependency graph for faster test runs startup ([#175](https://github.com/speedb-io/speedb/pull/175))
* Change the name of the output artifacts to Speedb ([#66](https://github.com/speedb-io/speedb/pull/66))

</details>

<details>

<summary>v2.0.0</summary>

Release date: 04-August-2022 | Based on RocksDB 7.2.2

#### New Features

* Added a new [hash based memtable ](/speedb-features/sorted-hash-memtable)that supports concurrent reads and writes
* Added ability to create MemTableFactory from URI/string to tools

#### Bug Fixes

* Avoid comparing Status using == as it compares only status codes. The comparison breaks when comparing against status::NoSpace() since it has a status code of `Code::kIOError` and only a subcode of `SubCode::kNoSpace`
* Fixed snapshots leak in optimistic\_transaction\_example: whenever the example is run under ASan, snapshots are acquired but not released, resulting in a memory leak error.
* ldb: fix get to print the entire value
* db\_bench: fix Rocksdb bug of last\_ref assertion. Test fails to delete multi-dbs correctly.
* db\_bench: fix SeekRandom and ReadRandomWriteRandom to work on all CFs instead of the default
* db\_bench to report accurate response time when using rate limit
* db\_test: add test for - forward the incomplete status on no\_io ([facebook/rocksdb#8485](https://github.com/facebook/rocksdb/pull/8485))
* CMake: use the old plugin infra and add support for \*\_FUNC registration

#### Miscellaneous

* LOG: Print write\_buffer\_manager size to LOG
* LOG: change log header to SpeeDB
* LOG & db\_bench: metadata\_cache\_options - print to LOG and support its configuration in db\_bench
* db\_impl: use unique\_ptr in DBImpl::Open for nicer memory management
* Explicitly compare the SuperVersion pointer in column\_family
* Rename rocksdb threads to speedb
* Add a version number to Speedb builds
* Clang-Format: Do not include third-party code as any changes are either version updates or fixes.
* Git: add clangd cache to .gitignore

</details>


# Roadmap

The [product roadmap](https://github.com/orgs/speedb-io/projects/4/views/1) provides a snapshot of the features we are currently developing, what we are planning for the future, and the items that have already been delivered.

We have added a column with items that are awaiting community feedback. We invite you to participate in our polls inside, share your thoughts about topics that are important to you, and let us know if there is anything else you would like to see on the list.

###


# Getting started

### [Speedb releases on GitHub](https://github.com/speedb-io/speedb/releases)

The following section covers how to get using Speedb:

* [Speedb example: ](/getting-started/speedb-example)the fastest way to get Speedb up and running
* [How to compile Speedb](/getting-started/how-to-compile-speedb)[ from source](/getting-started/how-to-compile-speedb)
* [Use prebuilt binaries ](/getting-started/install)
* [Drop-in replacement for Rocksdb:](/getting-started/drop-in-replacement) for existing rocksdb users that would like to start using Speedb with minimum steps&#x20;

###

Questions? Please feel free to contact us via [GitHub](https://github.com/speedb-io/speedb/discussions) or [Discord](https://discord.gg/52yzKZ5G9D).

{% hint style="info" %}
Please be sure to read and follow our [Code of Conduct](https://github.com/speedb-io/speedb/blob/main/CODE_OF_CONDUCT.md).
{% endhint %}


# Speedb Quick Start Example

```
git clone https://github.com/speedb-io/speedb
cd speedb
make static_lib
cd examples
make speedb_is_awesome_example
./speedb_is_awesome_example
```

**Code examples**

Full example can be found under <https://github.com/speedb-io/speedb/blob/main/examples/speedb\\_is\\_awesome\\_example.cc>

```cpp
#include <iostream>

#include "rocksdb/db.h"
#include "rocksdb/options.h"

using namespace ROCKSDB_NAMESPACE;

#if defined(OS_WIN)
std::string kDBPath = "C:\\Windows\\TEMP\\speedb_is_awesome_example";
#else
std::string kDBPath = "/tmp/speedb_is_awesome_example";
#endif
```

```cpp
// Open the storage
DB* db = nullptr;
Options options;
// create the DB if it's not already present
options.create_if_missing = true;
DB::Open(options, "/speedb", &db);     
```

```cpp
// append new entry 
std::string key = "key";
std::string val = "Speedb is awesome!"; 
db->Put(WriteOptions(), key, val);
```

```cpp
// retrieve entry 
std::string value; 
db->Get(ReadOptions(), "key", &value);    
```

```cpp
// close DB 
db->Close();
```


# Dependencies

Speedb's library should be able to compile without any dependency installed.

Although we recommend installing some compression libraries:

* [zlib](http://www.zlib.net/) - for data compression
* [bzip2](http://www.bzip.org/) - for data compression
* [lz4](https://github.com/lz4/lz4) - for extremely fast data compression
* [snappy](http://google.github.io/snappy/) - for fast data compression
* [zstandard](http://www.zstd.net) - for fast real-time compression algorithms
* All of our tools depend on [gflags](https://gflags.github.io/gflags/). This is a library that handles command line flag processing. Note that this is only required for building the tools, and that you can compile the Speedb library even if you don't have gflags installed.
* `make check` will also check code formatting, which requires [clang-format](https://clang.llvm.org/docs/ClangFormat.html)
* To build the RocksJava static target, then CMake is required for building Snappy
* If you want to run microbench (e.g, `make microbench`, `make ribbon_bench` or `cmake -DWITH_BENCHMARK=1`), Google benchmark >= 1.6.0 is needed.
* [Per Operating System](#supported-platforms)

## **Prerequisites:**

1. &#x20;gcc/clang with C++17 support (GCC >= 7, Clang >= 5).&#x20;

{% hint style="info" %}
Note: By default, the binary we produce is optimized for the platform you're compiling on (`-march=native` or the equivalent).
{% endhint %}

{% hint style="info" %}
Note: SSE4.2 will be enabled automatically if your CPU supports it.
{% endhint %}

{% hint style="info" %}
Note: To print a warning if your CPU doesn't support SSE4.2, build with `USE_SSE=1 make static_lib` or, if using CMake, `cmake -DFORCE_SSE42=ON`.
{% endhint %}

{% hint style="info" %}
If you want to build a portable binary, add `PORTABLE=1` before your make commands, as follows: `PORTABLE=1 make static_lib`, or `cmake -DPORTABLE=1` if using CMake.
{% endhint %}

## Supported Platforms

### **Linux - Ubuntu**

* Upgrade your gcc to version at least 7 to get C++17 support
* Install gflags:\
  First, try `sudo apt-get install libgflags-dev`\
  If this doesn't work and you're using Ubuntu, here's a good tutorial: (<http://askubuntu.com/questions/312173/installing-gflags-12-04>)
* Install Snappy:\
  This is usually done easily by running: `sudo apt-get install libsnappy-dev`.
* Install zlib:\
  Try `sudo apt-get install zlib1g-dev`
* Install bzip2: `sudo apt-get install libbz2-dev`
* Install lz4: `sudo apt-get install liblz4-dev`
* Install zstandard: `sudo apt-get install libzstd-dev`

### **Linux - CentOS / RHEL**

* Upgrade your gcc to version to at least 7 in order to get C++17 support
* Install gflags:

  ```
      git clone https://github.com/gflags/gflags.git
      cd gflags
      git checkout v2.0
      ./configure && make && sudo make install
  ```

  **Notice**: Once installed, add the include path for gflags to your `CPATH` environment variable and the lib path to `LIBRARY_PATH`.\
  If installed with default settings, the include path will be `/usr/local/include` and the lib path will be `/usr/local/lib`
* Install Snappy:

  ```
      sudo yum install snappy snappy-devel
  ```
* Install zlib:

  ```
      sudo yum install zlib zlib-devel
  ```
* Install bzip2:

  ```
      sudo yum install bzip2 bzip2-devel
  ```
* Install lz4:

  ```
      sudo yum install lz4-devel
  ```
* Install ASAN (optional for debugging):

  ```
      sudo yum install libasan
  ```
* Install zstandard:
  * With [EPEL](https://fedoraproject.org/wiki/EPEL):

    ```
    sudo yum install libzstd-devel
    ```
  * With CentOS 8:

    ```
    sudo dnf install libzstd-devel
    ```
  * From source:

    ```
    wget https://github.com/facebook/zstd/archive/v1.1.3.tar.gz
    mv v1.1.3.tar.gz zstd-1.1.3.tar.gz
    tar zxvf zstd-1.1.3.tar.gz
    cd zstd-1.1.3
    make && sudo make install
    ```

### **OS X**

* Install the latest C++ compiler that supports C++ 17:
  * Update XCode: Run `xcode-select --install` (or install it from XCode App's settting).
  * Install via [homebrew](http://brew.sh/):
    * If you're first time developer in MacOS, you still need to run: `xcode-select --install` in your command line.
    * Run `brew tap homebrew/versions; brew install gcc7 --use-llvm` to install gcc 7 (or higher).

### **Windows** (Visual Studio 2017 to up)

* Read and follow the instructions at [CMakeLists.txt](https://github.com/speedb-io/speedb/blob/main/CMakeLists.txt)


# How to Compile Speedb

This page describes how to clone, compile and use the library.

Check the prerequisites before you start compiling in the [dependencies](/getting-started/dependencies) page.&#x20;

### Compile in Release mode

* **Recommended:** `make static_lib` will compile the Speedb static library (`librocksdb.a`) in release mode.
* `make shared_lib` will compile the Speedb shared library (`librocksdb.so`) in release mode.

### Compile in Debug mode

{% hint style="info" %}
**Important**: If you plan to run Speedb in production, don't compile using the default `make` or `make all` invocations. This will compile Speedb in debug mode, which is much slower than release mode.
{% endhint %}

* `make check` will compile Speedb in debug mode and run all the unit tests.
* `make all` will compile Speedb's static library, and all tools and unit tests. These tools depend on gflags, so you'll need to have gflags installed to run `make all`. This will compile Speedb in debug mode. Also, please don't use binaries compiled by `make all` in production.

##


# Drop-in Replacement

## Replace your existing Rocksdb with Speedb

1. [Download the package](https://github.com/speedb-io/speedb/releases)
2. Copy the library along with the include folder to your project
3. Link it

That's it - you can now start using using Speedb!

Note: Speedb is fully compatible with Rocksdb. The changes can be reverted at any time, although we'd appreciate your feedback if, for any reason, you choose to revert.


# Use prebuilt binaries

1. Download and extract the [Speedb package](https://github.com/speedb-io/speedb/releases). You should have a `usr` directory with `include` and `lib` directories under it.
   * Add the path of the `include` directory to the compiler command line (this may depend on your build system; `-I` for GCC/Clang. Assuming you extracted the archive to `/home/user/speedb`, the flag should be `-I /home/user/speedb/usr/include`
   * Add the path to the `lib` directory and the library to the linker command line (this may depend on your build system. Assuming you extracted the archive to `/home/user/speedb`, for GCC/Clang this would be `-L /home/user/speedb/usr/lib` and `-lspeedb`, respectively)


# How to contribute

This page includes the ways you can contribute to the Speedb open-source project.

Thank you for your interest in contributing to Speedb! There are many ways to contribute, and we appreciate them all.

## Ways to contribute

There are several ways to contribute to Speedb:

* [Contribute code](/getting-started/how-to-contribute/contribute-code)
* [Feature request process](/getting-started/how-to-contribute/feature-request-process)
* [Submit a Pull Request](/getting-started/how-to-contribute/submit-a-pull-request)
* [Add or update documentation](/getting-started/how-to-contribute/add-or-update-documentation)
* [Report bugs and other issues](/getting-started/how-to-contribute/report-bugs-and-other-issues)
* [Help with new and ongoing feature development](/getting-started/how-to-contribute/help-with-new-and-ongoing-feature-development)
* [Check out a Pull Request](broken://pages/956CT56MKe1jsApXUpJ3) &#x20;

All documents and pull requests must be consistent with the guidelines and follow the Speedb documentation and coding styles.

For both documentation and code:

* Once the Speedb team accepts new documentation or features, we're responsible for the maintenance of said changes.  This means we'll weigh the benefit of each contribution against the cost of maintaining it.
* The appropriate style must be applied
* The license must be present in all contributions
* Code review is used to improve the correctness, clarity, and consistency of all contributions


# Contribute code

This page provides a basic introduction and guides on how to contribute to Speedb.

## Code Style

Speedb’s code follows the [Google C++ style guide](https://google.github.io/styleguide/cppguide.html), which you can read [more about here](https://google.github.io/styleguide/cppguide.html).

For formatting, we limit each line to 80 characters. Most formatting can be done automatically by running:

```
build_tools/format-diff.sh
```

Alternatively, if you use [GNU make](https://www.gnu.org/software/make/), simply run `make format`. If you lack any of the dependencies required to run the script, the script will print out instructions for you to install them.

## Contribution workflow

Like most open-source projects in GitHub, each Speedb contributor works on their own fork, sending pull requests to Speedb’s repo. Once a reviewer approves the pull request, a Speedb team member will merge it.

Read more about the [Pull Request](/getting-started/how-to-contribute/submit-a-pull-request) process here.

## Unit tests <a href="#unit-tests" id="unit-tests"></a>

If you make a code-related change, be sure to add a unit test for validation.

For new features, new unit tests or test scenarios must be added, even if the changes have been validated manually. This is to make sure future contributors can rerun the tests to validate that their changes don't cause any issues.

Speedb uses [GTest](https://github.com/google/googletest) for the C++ unit tests and [JUnit](https://junit.org/) for the Java unit tests.&#x20;

### C++ unit tests

For  C++ unit tests, it's preferable to add a test to an existing unit test suite (in files that end with `_test.cc`) in order to keep build and test time to a minimum.&#x20;

That said, if you're adding a test for a new feature and it doesn't belong in any of the existing test suites, you can add a new file.&#x20;

Be sure to update the `TEST_MAIN_SOURCES` variable in `[`[`src.mk`](https://github.com/speedb-io/speedb/blob/main/src.mk)`](<https://www.notion.so/zigel/src.mk>)` (note the backslashes at the end of each line) as well as the `TESTS` variable in `[`[`CMakeLists.txt`](https://github.com/speedb-io/speedb/blob/main/CMakeLists.txt)`](<https://www.notion.so/zigel/CMakeLists.txt>)`.

You can run the C++ unit tests using the Makefile as explained below, or, if you're using CMake, using `ctest`.&#x20;

The Makefile supports running the unit tests in parallel using GNU Parallel, so it's recommended that you install GNU Parallel first using your system's package manager (refer to the GNU Parallel [official webpage](https://www.gnu.org/software/parallel/) for more information).

In order to run unit tests execute the following command:

```
make check
```

This will build Speedb and run the tests. For better CPU utilization and to speed up the build, you can use the `-j` flag.

Note that this flag only affects the build, not the tests themselves. If you have GNU Parallel installed, you can control the number of parallel tests to run using the environment variable `J`. \
For example, to build on a 64-core CPU and run the tests in parallel, you can run:

```
make J=64 check -j64
```

Unlike `-j`, which, if not provided defaults to 1, if `J` isn't provided, one job will be run per core.

If you switch between release and debug build, normal or lite build, or compiler or compiler options, call `make clean` first.&#x20;

Here's a safe routine to run all tests:

```
make clean && make check -j64
```

### Debug single unit test failures

You can run a specific unit test by running the test binary that contains it.&#x20;

If you use GNU make, the test binary will be in the root directory of the repository. Note: If you use CMake, the test binary will be in your build directory.&#x20;

For example, the test `DBBasicTest.OpenWhenOpen` is in binary `db_basic_test`, so simply running the following will run all tests in the binary:

```
./db_basic_test
```

GTest provides some useful command line parameters. To view them, call `--help`:

```
./db_basic_test --help
```

The command line parameter that you're most likely to use is probably `--gtest_filter`, which allows you to specify a subset of the tests to run.&#x20;

For example, if you only want to run `DBBasicTest.OpenWhenOpen`:

```
./db_basic_test --gtest_filter="*DBBasicTest.OpenWhenOpen*"
```

By default, the test DB created by tests is cleared even if test fails. You can try to preserve it by using `--gtest_throw_on_failure`.&#x20;

If you want to stop the debugger when assert fails, specify `--gtest_break_on_failure`.

The `KEEP_DB=1` environment variable is another way to preserve the test DB from being deleted at the end of a unit-test run, regardless of whether the test fails or not:

```
KEEP_DB=1 ./db_basic_test --gtest_filter=DBBasicTest.Open
```

By default, the temporary test files will be under `/tmp/rocksdbtest-<number>/` (except when running in parallel, in which case they are under `/dev/shm`).&#x20;

You can override the location by using environment variable `TEST_TMPDIR`. For example:

```
TEST_TMPDIR=/dev/shm/my_dir ./db_basic_test
```

### Java unit tests

To run Java unit tests, run:

```
make jclean rocksdbjava jtest
```

Running with `-j` can sometimes cause issues. If this occurs, try removing `-j.`

### Additional build flavors

For more complicated code changes, we ask contributors to run more build flavors before sending the code for review.

To build with *AddressSanitizer (ASAN)*, set the environment variable `COMPILE_WITH_ASAN`:

```
COMPILE_WITH_ASAN=1 make check -j64
```

To build with *ThreadSanitizer (TSAN)*, set the environment variable `COMPILE_WITH_TSAN`:

```
COMPILE_WITH_TSAN=1 make check -j64
```

To run *UndefinedBehaviorSanitizer (UBSAN)*, set the environment variable `COMPILE_WITH_UBSAN`:

```
COMPILE_WITH_UBSAN=1 make check -j64
```

To run LLVM's analyzer, run:

```
make analyze
```

### Crash tests

For changes with higher risks, other than running all of the tests with multiple flavors, a crash test cycle needs to be executed without failure. If the crash test doesn't cover the new feature, add it there.

To run all crash tests, run:

```
make crash_test -j64
make crash_test_with_atomic_flush -j64
```

If you aren't able to use GNU make, you can manually build the `db_stress` binary, and run the following commands manually:

```
  python -u tools/db_crashtest.py whitebox
  python -u tools/db_crashtest.py blackbox
  python -u tools/db_crashtest.py --simple whitebox
  python -u tools/db_crashtest.py --simple blackbox
  python -u tools/db_crashtest.py --cf_consistency blackbox
  python -u tools/db_crashtest.py --cf_consistency whitebox
```

### Commit changes

Please keep your commits:

* Standalone - The code must compile and run successfully after each commit (no breaking commits!)
* Minimal - Break your code into minimal, logically-complete chunks
* Self-reviewed - Always double-check yourself before submitting

Commit messages should:

* Start with a component name followed by a colon. For example, if you made changes to the documentation, prefix the commit message with `docs:` \
  If you only updated tests, prefix the commit message with `tests:`\
  For build-related changed use `build:` , etc.
* Reference a relevant issue, if any. This is especially relevant for bug fixes and new features. The issue should be referenced at the end of the first line as a hash sign followed by the issue number. For example, `#23`.
* Have the line length limited to 100 characters or less. This restriction does not apply when quoting program output, etc.
* Please use clear and grammatically-correct language, and use present tense ("add feature", not "added feature".)

###


# Feature request process

This page describes the process for a new feature request.

If you have an idea for a new feature, we encourage you to first [discuss it with the Speedb community](https://github.com/speedb-io/speedb/discussions) regarding its viability and potential.&#x20;

Once you've discussed it with the community, you should prepare a proposal and submit it as a feature request. For your feature proposal, [use the feature request template](https://github.com/speedb-io/speedb/issues/new?assignees=\&labels=\&template=feature_request.md\&title=). \
\
When working on a proposal, it's important to keep in mind that it takes time to discuss new ideas with the community, review it, and eventually implement it.&#x20;

We encourage you to discuss your idea early, before even writing your proposal.&#x20;

Once your proposal is accepted, the next step is to [submit a pull request](/getting-started/how-to-contribute/submit-a-pull-request#important-information-when-creating-a-pull-request).&#x20;

### A few things to note

* Bug fixes and mechanical improvements don't need a feature request.
* All new features and bug fixes must include unit tests, since they help to (a) document and validate concrete usage of a feature and its edge cases, and (b) guard against future breaking changes to lower the maintenance cost.
* Unit tests must pass with the changes.
* If a feature with failed tests (unrelated to the feature) is submitted, it will only be considered once the feature passes all testing.
* Code changes should be made with API compatibility and evolvability in mind.

Pull requests should only be sent once a proposal has been discussed, submitted, and reviewed.

It's important to note that all new features and substantive changes to Speedb need to go through a formal feature request process.&#x20;


# Submit a pull request

This page describes the process for submitting a pull request for a code change.

When you're finished with your code changes, create a pull request, also known as a PR. If you're unfamiliar with open-source contributions on GitHub, follow the [Creating a pull request guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request).

#### Important information when creating a pull request

* Describe what your change achieves, especially if there isn't a relevant issue open.
* Reference relevant issues and discussions, and don't forget to [link your PR to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you're  solving one.
* Explain how you tested your changes (we recommend adding a "Test Plan:" section to the pull request summary, which specifies what testing was done to validate the quality and performance of the change).
* If your change impacts performance, explain why the specific performance environment was chosen. Also specify at least one benchmark test case that favors the improvement and share the results.
* Enable the checkbox to allow maintainer edits so the branch can be updated for a merge. Once you submit your PR, a Speedb team member will review your proposal. We may ask questions or request additional information.
* We may ask for changes to be made before a PR can be merged, either using suggested changes or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch.
* If you run into any merge issues, check out this [git tutorial](https://lab.github.com/githubtraining/managing-merge-conflicts) to help you resolve merge conflicts and other issues.

You can submit a [pull request here](https://github.com/speedb-io/speedb/pulls).&#x20;


# Add or update documentation

We strive to offer extensive and up-to-date documentation for Speedb, so if you find an area with missing or incomplete documentation, feel free to contribute.

All documentation is written for clarity and readability. Beyond fixing spelling and grammar, this also means that content is worded to be accessible to a broad audience.

Typos or other minor fixes that don't change the meaning of a document don't need a formal review, and can be handled directly as a pull request.

&#x20;[Speedb documentation Repository ](https://github.com/speedb-io/book)


# Report bugs and other issues

Like all software systems, we’ll inevitably experience bugs or other types of issues.

You can help by reporting bugs or issues with detailed information (ideally with a test case attached), or even simply analyzing and reproducing an existing issue, is a great way to get involved.

We track bugs and other kinds of issues using [GitHub issues](https://github.com/speedb-io/speedb/issues).

{% hint style="info" %}
Please be sure to review existing issues before opening a new one to avoid duplicates, and follow the relevant template when opening new issues.
{% endhint %}

You can report an [issue here](https://github.com/speedb-io/speedb/issues/new/choose).


# Help with new and ongoing feature development

If you find a feature request that you'd like to work on, and there's a pull request open for it, you can help by testing it and providing feedback. When giving feedback, please keep comments positive and constructive.

### How to check out a pull request

Every pull request has its own number, which  is visible both in the URL\
of a pull request page as well as in the title of the pull request page itself\
(the format is **#123**, where 123 is the PR number).&#x20;

To check out a pull request locally, [follow this guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally).&#x20;

If you're using GitHub CLI, be sure to choose the **GitHub CLI** option rather than **Web Browser** on the guide page. Once you have the pull request changes checked out locally, you can move on to\
[testing the changes](/getting-started/how-to-contribute/contribute-code#unit-tests).


# Kafka Streams: How to use Speedb instead of RocksDB?

By default, Kafka streams uses RocksDB for state store. Speedb is a fully compatible fork of RocksDB that introduces overall performance improvements when used instead. By replacing RocksDB with Speedb you can enjoy the improved performance Speedb offers. <br>

There are 3 options for replacement of RocksDB in Kafka 3.3 and higher:

1. Rebuild Kafka streams with Speedb
2. Replace the RocksDB library with Speedb library
3. Use the prebuilt Kafka+Speedb version&#x20;

### Rebuild Kafka with Speedb

If you are building from source, you need to modify gradle/dependencies.gradle to point to Speedb instead of RocksDB and indicate version 2.4.1.5 instead of 7.1.2:

\- rocksDBJni: "org.rocksdb:rocksdbjni:$versions.rocksDB",&#x20;

\+ rocksDBJni: "io.github.speedb-io:speedbjni:$versions.rocksDB",&#x20;

\- rocksDB: "7.1.2",&#x20;

\+ rocksDB: "2.4.1.5",<br>

### Replace the library

If you are using a prebuilt  Kafka version, replace the file libs/rocksdbjni-7.1.2.jar with the jar downloaded from maven at the following URL:

[https://repo1.maven.org/maven2/io/github/speedb-io/speedbjni/2.4.1.5/](https://repo1.maven.org/maven2/io/github/speedb-io/speedbjni/2.4.1.1/)&#x20;

(the downloaded speedbjni file should be renamed to rocksdbjni-7.1.2.jar)

### Use the prebuilt Kafka+Speedb version&#x20;

Click [Here](https://storage.googleapis.com/speedb-artifacts/kafka/213_26/kafka_2.13-3.5.1.tgz) to download the latest compiled version.&#x20;

<br>


# Speedb Features


# Memory Tracking

### Objectives

The purpose of this feature is to provide insight about the memory consumption of each component. This should be useful when debugging memory issues&#x20;

### How to enable it?

In order to enable the memory reporting, you should add a compilation flag:

*MEMORY\_REPORTING=1 make -j $(NPROC)*&#x20;

### Technical Details&#x20;

An arena is a contiguous piece of memory, which Speedb allocates once and use part of the memory to minimize number of allocations requested from the OS

The memory counter is printed to the log by default every 10 minutes.&#x20;

* This is a configurable parameter, you can change it under:

&#x20;      *options.stats\_dump\_period\_sec* (the default of this option is 600, 10 minutes),

In order to get prints more often, the counter should be reduced.

<br>

#### Counters info:

* These metrics have been chosen to be part of the memory reporting feature because they represent most of the memory allocated in Speedb.
* Under Arena stats, there is memory allocated by the Arena, The total usage and per component that allocates using Arena.
* Each line under the Arena Stats describes the distribution of allocations requested (and not deallocated yet) per component in the code.
  * CF Stats - shows Memtable memory allocation per CF and summarizes all CFs together.
  * rocksdb.block-cache-usage - Total block cache usage reporting
  * rocksdb.estimate-table-readers-mem - How much memory is allocated to indexes and filter blocks
  * rocksdb.block-cache-pinned-usage - Pinned block cache usage
  * CacheAllocationUsage - Total memory allocated by BlockFetcher

**Example for general output:**&#x20;

\*\* Memory Reporting \*\*

Arena Stats:

Total: 16M

ArenaWrappedDBIter: 0

FileIndexer::UpdateIndex: 0

MemTable::NewIterator: 0

LevelFileInit: 0

FindLevelFileTest::Add: 0

DoGenerateLevelFilesBrief: 0

TEST\_GetLevelIterator: 0

Version::AddIteratorsForLevel: 0

Version::OverlapWithLevelIterator: 0

LogBuffer::AddLogToBuffer: 0

arena\_test: 0

HashLinkList: 0

HashLinkListIterator: 0

HashLinkListDynamicIterator: 0

HashSkipList: 0

HashSkipListIterator: 0

HashSkipListDynamicIterator: 0

HashSpdb: 16M

HashSpdbIterator: 0

InlineSkipList: 0

SkipList: 0

SkipListIterator: 0

SkipListLookaheadIterator: 0

VectorMemtable: 0

CompactionMergingIterator: 0

NewErrorInternalIterator: 0

NewEmptyInternalIterator: 0

MergingIterator: 0

BlockBasedTableIterator: 0

BlockPrefixIndex::Builder: 0

CuckooTableIterator: 0

PlainTableBloomV1: 0

PlainTableIndexBuilder::FillIndexes: 0

PlainTableReader::NewIterator: 0

DynamicBloom: 0

DefaultMemtableImpl: 0

WriteBatchWithIndex: 0

CF Stats:&#x20;

Total: 16M

\[default]: 16M

rocksdb.block-cache-usage: 96

rocksdb.estimate-table-readers-mem: 659K

rocksdb.block-cache-pinned-usage: 96

Total CacheAllocationUsage: 655K

#### Jmalloc

\
In addition when Jemalloc is being used as an allocator for Speedb, there are more memory reporting metrics, in order to enable this dump please set option.dump\_malloc\_stats to true.

*Allocated: 262920624, active: 264196096, metadata: 7739440 (n\_thp 0), resident: 273063936, mapped: 307642368, retained: 79806464*

**Allocated -** Number of bytes allocated by the Rocksdb

**Active -** Number of bytes allocated actively (includes the entire pages allocated size)<br>

**Metadata** - Jemalloc itself metadata to manage memory allocation

**Resident** - Maximum overall size allocated includes zeroed pages including metadata (which may not reserve actual memory).

**Mapped** - Mapped is the sum of regions of virtual address space currently dedicated (internally) to serving some live allocation. - from jemalloc git

**Retained** - Freed memory that Jemalloc didnt return to the OS

**Source: <https://jemalloc.net/jemalloc.3.html#stats.allocated>**

\ <br>


# Speedb Tuning Function

## Overview

This feature aims to simplify the configuration process for Speedb storage engine by providing an easy way to enable and set Speedb options to optimized configurations. By introducing the SharedOptions class and specific functions, users can effortlessly configure Speedb's features with minimal manual intervention. Also when running multiple Speedb instances, it allows to configure all of the instances using the same method, and same configuration.&#x20;

## Motivation&#x20;

Speedb users currently configure the database manually. New Speedb users are required to spend time in order to understand how to enable/disable and actually configure all the relevant options to enjoy the capabilities.&#x20;

Not all of the capabilities are enabled by default and some need different tuning per workload. The user always has the flexibility to tune the database based on their needs, but in case a user would like to enjoy all of Speedb's benefits, we give the option to let us do the work for you and tune the database or databases to a default and improved configuration, following basic system parameters you provide while using this new method.&#x20;

## Technical Details

**SharedOptions Class**

The SharedOptions class has been introduced to enhance the usability of Speedb in multiple database scenarios. It organizes shared options, making configuration more streamlined and user-friendly.

### Configuration Parameters

To benefit from this feature, users need to provide three key configuration parameters:

* **Total RAM size for Speedb:** Specify the amount of RAM allocated for Speedb operations.
* **Total number of threads for background jobs**: Determine the number of threads allocated for handling background tasks.
* **Delayed write rate:** Set the rate at which delayed writes are performed.

By utilizing the provided functions, Speedb's features can be easily configured to default settings, simplifying the process for users. The default configuration includes the following features:

* Proactive Flushing
* Global Delayed Write
* Sorted Hash Memtable
* Dynamic Delayed Writes
* Paired bloom filter

### Implementation&#x20;

*Struct SharedOptions*&#x20;

* Contain the shared configuration for multiple databases group
  * Write buffer manager
  * Cache
  * Write controller
  * Env;
  * RateLimiter;
  * SstFileManager;
  * Logger;
  * EventListener;
  * FileChecksumGenFactory;
* Contain the methodology for configuring Speedb features&#x20;
  * total\_threads
  * total\_ram\_size\_bytes
  * delayed\_write\_rate
  * Write\_buffer\_size
  * Paired bloom filter

**class SharedOptions**

```cpp
class SharedOptions {
public:
SharedOptions();
SharedOptions(size_t total_ram_size_bytes, size_t total_threads,
size_t delayed_write_rate = 256 * 1024 * 1024ul);
size_t GetTotalThreads() { return total_threads_; }
size_t GetTotalRamSizeBytes() { return total_ram_size_bytes_; }
size_t GetDelayedWriteRate() { return delayed_write_rate_; }
// this function will increase write buffer manager by increased_by amount
// as long as the result is not bigger than the maximum size of
// total_ram_size_ /4
void IncreaseWriteBufferSize(size_t increase_by);


std::shared_ptr<Cache> cache = nullptr;
std::shared_ptr<WriteController> write_controller = nullptr;
std::shared_ptr<WriteBufferManager> write_buffer_manager = nullptr;
Env* env = Env::Default();
std::shared_ptr<RateLimiter> rate_limiter = nullptr;
std::shared_ptr<SstFileManager> sst_file_manager = nullptr;
std::shared_ptr<Logger> info_log = nullptr;
std::vector<std::shared_ptr<EventListener>> listeners;
std::shared_ptr<FileChecksumGenFactory> file_checksum_gen_factory = nullptr;


private:
size_t total_threads_ = 0;
size_t total_ram_size_bytes_ = 0;
size_t delayed_write_rate_ = 0;
};

```

**SharedOptions constructor**

* *Creates a LRU cache*\
  *Default values for cache’s construction is:*&#x20;
  * *Capacity = \_total\_ram\_size\_bytes*
* *Creates a write buffer manager (with proactive flushes)* \
  *Default values for WBM’s construction is:*&#x20;
  * *\_write\_buffer\_size = 1;*
  * *kDfltMaxNumParallelFlushes = 4U;*
* *Creates a write\_controller* \
  *default values for WC’s construction is:*&#x20;
  * *dynamic\_delay = true*
  * *\_delayed\_write\_rate = 256 \* 1024 \* 1024ul*

**EnableSpeedbFeatures function**

* *Call EnableSpeedbFeaturesDB*
* *Call EnableSpeedbFeaturesCF*

```cpp
Options* Options::EnableSpeedbFeatures(SharedOptions& shared_options){…
}

```

&#x20;\
**EnableSpeedbFeaturesDB function**

* *Set TotalThreads;*
* *Set delayed\_write\_rate;*
* *bytes\_per\_sync = 1ul << 20;*
* *use\_dynamic\_delay = true;*
* *Set write\_buffer\_manager;*
* *Set write\_controller;*<br>

**DBOptions\* DBOptions::EnableSpeedbFeaturesDB(SharedOptions& shared\_options)**

```cpp
DBOptions* DBOptions::EnableSpeedbFeaturesDB(SharedOptions& shared_options) {
 …
IncreaseParallelism((int)shared_options.GetTotalThreads());
delayed_write_rate = shared_options.GetDelayedWriteRate();
bytes_per_sync = 1ul << 20;
use_dynamic_delay = true;
write_buffer_manager = shared_options.write_buffer_manager;
write_controller = shared_options.write_controller;
 …
}
```

\
**EnableSpeedbFeaturesCF function**

* *Each new column family will ask the write buffer manager to increase the write buffer size by 512 \* 1024 \* 1024ul*
* *Set cf write\_buffer\_size min(db\_wbf\_size / 4, 64ul << 20);*
* *max\_write\_buffer\_number = 32;*
* *min\_write\_buffer\_number\_to\_merge = max\_write\_buffer\_number - 1;*
* *// set the pinning option for indexes and filters*
* *Set filter to FilterPolicy to speedb.PairedBloomFilter 10 bytes per key*
* *Set cache*
* *Pinned all tables info (index, filter, compression dictionary)*
  * *Set unpartitioned\_pinning to PinningTier::kAll*
  * *Set partition\_pinning to PinningTier::kAll*
  * *Pinned by set to CacheEntryRoleOptions::Decision::kEnabled*
    * &#x20;*CacheEntryRole::kFilterConstruction*
    * *CacheEntryRole::kBlockBasedTableReader*
    * *CacheEntryRole::kCompressionDictionaryBuildingBuffer*
    * *CacheEntryRole::kFileMetadata*
* *Make sure pinned memory is accounted toward the cache*
* *Set memtable type to speedb.HashSpdRepFactory*

\ <br>

**ColumnFamilyOptions\* ColumnFamilyOptions::EnableSpeedbFeaturesCF(SharedOptions& shared\_options)**

```cpp
ColumnFamilyOptions* ColumnFamilyOptions::EnableSpeedbFeaturesCF(
SharedOptions& shared_options) {
…
shared_options.IncreaseWriteBufferSize(512 * 1024 * 1024ul);
auto db_wbf_size = shared_options.write_buffer_manager->buffer_size();
// cf write_buffer_size
write_buffer_size = std::min<size_t>(db_wbf_size / 4, 64ul << 20);
max_write_buffer_number = 32;
min_write_buffer_number_to_merge = max_write_buffer_number - 1;
// set the pinning option for indexes and filters
…
Set config_options "speedb.PairedBloomFilter:10" filter_policy);
…
block_based_table_options.metadata_cache_options.unpartitioned_pinning =
PinningTier::kAll;
block_based_table_options.metadata_cache_options.partition_pinning =
PinningTier::kAll;
block_based_table_options.block_cache = shared_options.cache;
auto& cache_usage_options = block_based_table_options.cache_usage_options;
CacheEntryRoleOptions role_options;
role_options.charged = CacheEntryRoleOptions::Decision::kEnabled;
cache_usage_options.options_overrides.insert(
{CacheEntryRole::kFilterConstruction, role_options});
cache_usage_options.options_overrides.insert(
{CacheEntryRole::kBlockBasedTableReader, role_options});
cache_usage_options.options_overrides.insert(
{CacheEntryRole::kCompressionDictionaryBuildingBuffer, role_options});
cache_usage_options.options_overrides.insert(
{CacheEntryRole::kFileMetadata, role_options});
…
std::string memtablerep = "speedb.HashSpdRepFactory";
…
```

**SharedOptions constructor**

* *Creates a LRU cache*\
  *default values for cache’s construction is:*&#x20;
  * *Capacity = \_total\_ram\_size\_bytes*
* *Creates a write buffer manager (with proactive flushes)* \
  *default values for WBM’s construction is:*&#x20;
  * *\_write\_buffer\_size = 1;*
  * *kDfltMaxNumParallelFlushes = 4U;*
* *Creates a write\_controller* \
  *Default values for WC’s construction is:*&#x20;
  * *dynamic\_delay = true*
  * *\_delayed\_write\_rate = 256 \* 1024 \* 1024ul*

{% hint style="info" %}
Note: The user is still able to configure any non-shared parameter as she is currently doing.&#x20;
{% endhint %}

<br>

### Usage

Below is an example  demonstrating the usage of these functions and enabling Speedb features with default configurations. It is also available in the examples directory /enable\_speedb\_features\_example.cc". <br>

```cpp

#include <cstdio>
#include <iostream>
#include <string>

#include "rocksdb/compression_type.h"
#include "rocksdb/db.h"
#include "rocksdb/options.h"
#include "rocksdb/slice.h"

using namespace ROCKSDB_NAMESPACE;

#if defined(OS_WIN)
std::string kDBPath1 = "C:\\Windows\\TEMP\\enable_speedb_features_example1";
std::string kDBPath2 = "C:\\Windows\\TEMP\\enable_speedb_features_example2";
std::string kDBPath3 = "C:\\Windows\\TEMP\\enable_speedb_features_example3";
std::string kDBPath4 = "C:\\Windows\\TEMP\\enable_speedb_features_example4";
#else
std::string kDBPath1 = "/tmp/enable_speedb_features_example1";
std::string kDBPath2 = "/tmp/enable_speedb_features_example2";
std::string kDBPath3 = "/tmp/enable_speedb_features_example3";
std::string kDBPath4 = "/tmp/enable_speedb_features_example4";
#endif

int main() {
  DB *db1 = nullptr;
  DB *db2 = nullptr;
  DB *db3 = nullptr;
  DB *db4 = nullptr;
  Options op1;
  Options op2;
  Options op3;
  Options op4;
  size_t total_ram_size_bytes = 512 * 1024 * 1024;
  size_t delayed_write_rate = 256 * 1024 * 1024;
  size_t total_threads = 8;

  // define SharedOptions object for each databases group
  SharedOptions so1(total_ram_size_bytes, total_threads, delayed_write_rate);

  // customize each options file except SpeedbSharedOptiopns members
  // as listed in the definition of SpeedbSharedOptiopns in options.h
  op1.create_if_missing = true;
  op1.compression = rocksdb::kNoCompression;
  //...
  op1.EnableSpeedbFeatures(so1);

  op2.create_if_missing = true;
  op2.compression = rocksdb::kZlibCompression;
  //...
  op2.EnableSpeedbFeatures(so1);

  // open the databases
  Status s = DB::Open(op1, kDBPath1, &db1);
  if (!s.ok()) {
    std::cerr << s.ToString() << std::endl;
    return 1;
  }

  s = DB::Open(op2, kDBPath2, &db2);
  if (!s.ok()) {
    std::cerr << s.ToString() << std::endl;
    return 1;
  }
  std::cout << "DBs group 1 was created" << std::endl;

  // do the same for any group of databases
  total_ram_size_bytes = 1024 * 1024 * 1024;
  delayed_write_rate = 128 * 1024 * 1024;
  total_threads = 4;
  SharedOptions so2(total_ram_size_bytes, total_threads, delayed_write_rate);

  // again customize each options object except SharedOptiopns members
  op3.create_if_missing = true;
  op3.compaction_style = rocksdb::kCompactionStyleUniversal;
  //...
  op3.EnableSpeedbFeatures(so2);

  op4.create_if_missing = true;
  op4.compaction_style = rocksdb::kCompactionStyleLevel;
  //...
  op4.EnableSpeedbFeatures(so2);

  // open the databases
  s = DB::Open(op3, kDBPath3, &db3);
  if (!s.ok()) {
    std::cerr << s.ToString() << std::endl;
    return 1;
  }

  s = DB::Open(op4, kDBPath4, &db4);
  if (!s.ok()) {
    std::cerr << s.ToString() << std::endl;
    return 1;
  }
  std::cout << "DBs group 2 was created" << std::endl;

  // creation of column family
  rocksdb::ColumnFamilyOptions cfo3(op3);
  rocksdb::ColumnFamilyHandle *cf;
  // coustomize it except SpeedbSharedOptiopns members

  // call EnableSpeedbFeaturesCF and supply for it the same SharedOptions
  // object as the DB, so2 this time.
  cfo3.EnableSpeedbFeaturesCF(so2);
  // create the cf
  s = db3->CreateColumnFamily(cfo3, "new_cf", &cf);
  if (!s.ok()) {
    std::cerr << s.ToString() << std::endl;
    return 1;
  }
  std::cout << "new_cf was created in db3" << std::endl;

  s = db3->DropColumnFamily(cf);
  if (!s.ok()) {
    std::cerr << s.ToString() << std::endl;
    return 1;
  }
  db3->DestroyColumnFamilyHandle(cf);
  if (!s.ok()) {
    std::cerr << s.ToString() << std::endl;
    return 1;
  }
  std::cout << "new_cf was destroyed" << std::endl;

  s = db1->Close();
  assert(s.ok());
  s = db2->Close();
  assert(s.ok());
  s = db3->Close();
  assert(s.ok());
  s = db4->Close();
  assert(s.ok());

  delete db1;
  delete db2;
  delete db3;
  delete db4;

  return 0;
}
```

<br>


# Table Pinning Policy

## Overview&#x20;

Speedb allows the user to specify an alternative policy to control how table memory is pinned.  Pinned memory is not subject to lookup or expiration by the cache and provides quicker access.  Because pinned memory is a more limited resource than cache, the use of it should be more controlled.<br>

## Motivation

Today there is an option to pin items to the cache, to increase performance.&#x20;

The user needs to decide what type of items to pin to the cache and what should be left to the block cache to manage.  The value of pinning is that it provides better performance while the downside is that the database may allocate memory more than the user intended and may cause a catastrophic out of memory event. Therefore, in many environments the users give up the performance gain of pinning, in order to get a predictable amount of memory while in others the users allocate a much smaller cache than it may in order to have safety margins. This new pinning control mechanism allows pinning index and filters up to a defined threshold so the user can enjoy the performance benefits of pinning without getting into out of memory condition.&#x20;

## Details&#x20;

Speedb provides a new scoped table pinning policy that pins data based on its level and how much data is currently pinned.  The policy has the following properties:

* Capacity: How much data can be pinned.  The capacity should be set to a percentage of the cache size;
* Bottom Percent: What percentage of the pinned capacity can be used by data at the bottom-most level of the LSM tree.  If pinned memory exceeds this capacity, data at the bottom-most level will no longer be pinned;
* Mid Percent: What percentage of the pinned capacity can be used by data in the mid levels (not L0 and not bottom most). &#x20;

This scoped pinning policy looks at the type and level of the data to be pinned and determines, based on this information and how much data is already pinned, if the new data should be pinned as well.

## Usage

A TablePinningPolicy is associated with a BlockBasedTableOptions.  If not specified, the default policy (matching the existing RocksDB behavior) is used.  The new Speedb scoped pinning policy can be created via TablePinningPolicy::CreateFromString.  For example, the call

<mark style="color:blue;">`TablePinningPolicy::CreateFromString(...,`</mark>&#x20;

&#x20; <mark style="color:blue;">`“id=speedb_scoped_pinning_policy; capacity=1G; bottom_percent=10;`</mark>

&#x20;  <mark style="color:blue;">`mid_percent=75”, …)`</mark>&#x20;

creates a new scoped pinning policy that will:

* Pin up to 1G of memory;
* Limit the bottom-most level to no more than 10% of 1G
* Limit the mid-level pinning to 75% of 1G.

By sharing the same pinning policy amongst the process.  If not specified, the default capacity is 3G, the default bottom and mid percentages are 10% and 80%, respectively.&#x20;

In order to get accurate accounting of memory, set “cache\_index\_and\_filter\_blocks=true” when using the scoped pinning policy.

<br>


# Snapshot Optimization

## Overview

This documentation provides an overview of a performance enhancement feature aimed at improving the performance of snapshots in transactional databases. The enhancement leverages previous snapshots to avoid unnecessary database mutex acquisition, leading to improved performance in scenarios where snapshot periods are short and few changes occur.

The goal of this enhancement is to optimize snapshot performance by utilizing previous snapshots when no changes have occurred since the last one. By avoiding the acquisition of a new database mutex and reusing the previous snapshot, performance is improved, especially in scenarios with read-intensive operations and a multithreaded environment.

## Motivation:

When taking a snapshot in a database, acquiring a database mutex has a performance impact on the entire system. In transactional databases, where maintaining data consistency is crucial, snapshots are taken for each transaction to ensure that operations work on a locked copy. This results in frequent acquisition of the database mutex, affecting overall performance.<br>

The improvement allows the database to use the previous snapshots taken, if  no changes were made since then and by that to avoid another db mutex. New snapshot would not be taken when the last snapshot has the same sequence number as a new one.

When transactional databases use mostly read operations, it improves performance when used with a multithreaded environment and as well other scenarios of taking large amounts of snapshots with mostly read operations.

The most important information inside a snapshot is its Sequence number, which allows the compaction to know if the key-value should be deleted or not.

The sequence number is being changed when modification happens in the db.

## Feature Details

Snapshot Reuse:&#x20;

The enhancement introduces the capability for the database to reuse the previous snapshot if no changes have been made since its creation. This is determined by comparing the sequence number of the last snapshot with the new snapshot. If they have the same sequence number, indicating no modifications, a new snapshot is not taken, and the previous snapshot is utilized.

Importance of Sequence Number

The sequence number plays a vital role in the snapshot performance enhancement. It allows the compaction process to determine whether a key-value pair should be deleted or retained. The sequence number is incremented whenever a modification occurs in the database, ensuring accurate tracking of changes.

## Prerequisites&#x20;

\*\*This Feature must have folly library installed.\*\*

To utilize this feature, the Folly library must be installed. Ensure that the necessary dependencies and configurations are in place before enabling the snapshot performance enhancement.<br>

Folly (acronymed loosely after Facebook Open Source Library) is a library of C++14 components designed with practicality and efficiency in mind. Folly contains a variety of core library components used extensively at Facebook. In particular, it's often a dependency of Facebook's other open source C++ efforts and a place where those projects can share code.

## How to install folly on Ubuntu:

```bash
sudo apt install libssl-dev libfmt-dev
git clone https://github.com/facebook/folly
cd folly
sudo ./build/fbcode_builder/getdeps.py install-system-deps --recursive
mkdir build_
cd build_
cmake .. -DBUILD_SHARED_LIBS=ON
make -j $(nproc) install

```

If there is an error with the compile process above that is related with fmt,&#x20;

Please run:

```bash
 sed -i "s/format_to/fmt::format_to/g" /usr/include/fmt/chrono.h
```

## **To compile Speedb with the snapshot optimization enhancement please compile it using cmake:**

```bash
git clone https://github.com/speedb-io/speedb
cd speedb
mkdir build
cd build
cmake .. -DWITH_SNAP_OPTIMIZATION=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release
make -j $(nproc)
```

## How has it been implemented?

In order to cache the snapshots, there is last\_snapshot\_

(folly::atomic\_shared\_ptr, lock free atomic\_shared\_ptr) in order to

access the last\_snapshot\_ created and point to it.

For every GetSnapshotImpl call (where snapshots are being created), the

function checks if the sequence number is different than last\_snapshot\_,

if no, it creates new snapshot and inside this snapshot it adds a

reference to last\_snapshot\_ (the reference is cached\_snapshot), so this sequence number will remain inside

SnapshotList (SnapshotList is the list of the snapshots in the system and used in compaction to show which snapshots are being used), if there are still snapshots holding this sequence number. If the sequence number has changed or the last\_snapshot\_ is nullptr it will create the snapshot while acquiring db\_mutex.

For ReleaseSnapshotImpl (deleting a snapshot).

We will unref the last\_snapshot\_ (using comapre\_exchange\_weak) and if the refcount becomes 0, it will

call Deleter and remove this snapshot entirely from the SnapshotList and

continue with taking the db mutex.

If there are still references, it will return without taking it out from

the SnapshotList nor taking the db mutex

## Test

The test was running on Azure Standard\_L64s\_v3 machine.\
Each thread in every test is creating 1M snapshots and delete those.

## Test results&#x20;

The following graph compares the time in seconds between RocksDB and Speedb while running with different numbers of threads.

As can be seen in the graph below, Speedb performs the operation in less than 60 seconds, regardless of the number of threads running while RocksDB reached to almost 2 minutes when the number of threads increased.  \
Threads here are transactions that requires snapshots. \ <br>

<figure><img src="https://lh5.googleusercontent.com/oGBwfNXYg_50Ckc4U86y5cUsKIu7i_HURseldVRYOUXPd5x-ghOWFqQiv-mTd_RmjumH3mHtJE0y-cWLgOn4AlfKQbpfXvehyROwwYgNmyyYE1c7jcHZ4BdrAsslDFuqS9DNnBgSiF8MC5eKwlz_Iaw" alt=""><figcaption></figcaption></figure>

User and system time graph shows that with Speedb, the performance is much higher without taking any extra CPU resources.&#x20;

<figure><img src="https://lh4.googleusercontent.com/rAxcWrb9n7MggucPlbKEfiLrtJld14rdvOAIHcP1r_GrPPgwcJKsIc-_6FELKBUgNWb43J0jYWJWx54hFaqhDzIqFtYPbQbg4JuHvuw9cUhg0OaSCCk5nAZhtR3WoPDzcPs1Lo7kQ8H-atsmdrHLVc8" alt=""><figcaption></figcaption></figure>

## Best practices&#x20;

The feature is mostly beneficial when:

* You are using snapshots with mostly read workload&#x20;

Example: when using transactional databases

\
\
\
\ <br>


# On Thread Start Callback

## Motivation:

This is a community driven feature following issue Github [#476](https://github.com/speedb-io/speedb/issues/476).&#x20;

This feature provides the ability to limit the Speedb threads to run on specific cores in order to gain performance or better control CPU usage.

## Solution:

Speedb introduces a new capability that allows Speedb users to configure a callback function that will run for each Speedb thread that is being spawned.

Using this callback a user could configure the thread affinity the way the user wants.<br>

## How to use it?

An example is also available under: examples/on\_thread\_start\_callback\_example.cc

{% code fullWidth="false" %}

```cpp
//  This example will pin all Speedb threads to the first core


#include <functional>
#include <iostream>
#include <memory>

#include "rocksdb/db.h"
#include "rocksdb/options.h"

using namespace ROCKSDB_NAMESPACE;

#if defined(OS_WIN)
std::string kDBPath = "C:\\Windows\\TEMP\\speedb_thr_affinity";
#else
std::string kDBPath = "/tmp/speedb_thr_affinity";
#endif

int main() {
  // Open the storage
  DB* db = nullptr;
  Options options;
  // create the DB if it's not already present
  options.create_if_missing = true;
  auto f = [](std::thread::native_handle_type thr) {
// callback to pin all Speedb threads to the first core.
#if defined(OS_WIN)
#include "winbase.h"
    SetThreadAffinityMask(thr, 0);
#else
#include "pthread.h"
    std::cout << "thread spawned, thread_id: " << thr << std::endl;
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(0, &cpuset);
    pthread_setaffinity_np(thr, sizeof(cpu_set_t), &cpuset);
#endif
  };
  options.on_thread_start_callback =
      std::make_shared<std::function<void(std::thread::native_handle_type)>>(f);
  Status s = DB::Open(options, kDBPath, &db);
  assert(s.ok());

  // append new entry
  std::string key = "key_1";
  std::string put_value = "Speedb is awesome!";
  s = db->Put(WriteOptions(), key, put_value);
  assert(s.ok());

  // retrieve entry
  std::string get_value;
  s = db->Get(ReadOptions(), key, &get_value);
  assert(s.ok());
  assert(get_value == put_value);
  std::cout << get_value << std::endl;

  // close DB
  s = db->Close();
  assert(s.ok());
  return 0;
}

```

{% endcode %}

\
\ <br>


# Write Flow

This feature is experimental in v2.4.0

## Introduction:

The SPDB Write Flow introduces a significant improvement in the performance of data writing in RocksDB. The SPDB Write Flow is a new approach to writing data in RocksDB that aims to reduce the amount of time spent holding global database mutexes and increase the level of parallelism for IO writes. This new approach is implemented in a dedicated SPDB write thread, which handles the memtable switch, flush requests, WAL (Write Ahead Log) switch, WBM threshold limit, write delay, and WAL trim.

## Background:

In the previous write flow, each write was inserted into a matching write queue, and a relevant thread queue picked a write group leader. Other writes were then connected to this leader as group members, and the parallel work was done until the limit was reached. The leader was responsible for WAL writing and waiting for all group members to complete their task. The version progressed when the group completed its task. However, this approach had several disadvantages, such as the blocking of new writes due to the memtable switch status check and the serial execution caused by the many DB mutex points.

## Algorithm:

The new SPDB Write Flow algorithm consists of a thread dedicated to handling the write flow. This thread wakes up at a specified time and handles quiesce, which includes the memtable switch, flush requested, WAL switch, WBM threshold limit, write delay, and WAL trim. The thread then proceeds to handle the batch writes by inserting them into two containers, one for the current batch writes and one for the new batch writes that are being inserted.

Each batch write is responsible for writing to the memtable (if needed) and waiting for the complete batch group. The batch group leader is, by default, the first one unless new batches are inserted in parallel, and the one batch group limit size is not reached. The batch leader is responsible for the merged group WAL write and writing to WAL with or without synchronization. It is important to note that WAL writes can be completed before all the container memtable writes are completed, which is okay. However, we must wait for the memetable writes to be completed to progress the version. The flow handles merge/none memtable writes/none WAL writes in the same container, allowing for a fluent flow. Still, writes to the WAL should consider that we build the merged WAL batch (seq number).

\
The table below summarize the differences between RocksDB write flow and the Speedb write flow:

|                                                 | RocksDB                                                     | Speedb                                                             |
| ----------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------ |
| General                                         | DBmutex on every write batch                                | RW lock - write lock when needed                                   |
| Writes to the WAL                               | Append, sync writes                                         | <p>Writing to a specific address space, parallel writes</p><p></p> |
| Checking the triggers                           | <p>Part of the DB mutex on every write</p><p></p>           | <p>Background, write lock only when needed </p><p></p>             |
| <p>Switch memtable/switch WAL/Trim WAL <br></p> | <p>Part of the DB mutex on every write</p><p></p>           | <p>Background, without any locks </p><p></p>                       |
| Writes  rollback                                | <p>Don’t needed (since writing first to the WAL)</p><p></p> | <p>Needed when write to the memtable failed </p><p></p>            |

To summaries, the new write flow enables parallel writes by the following changes:

1. Speedb changed the DB mutex to read/write-lock
2. Speedb write flow allows parallel writes to the memtable and the WAL. Ack is sent only after the data is written to both, but the writes are no longer serial.&#x20;
3. The wall in the previous write flow is using append, meaning only single write is allowed at a time. Speedb's new write flow changed the way data is written to the WAL and now writes to a specific address in the file, a fact that enables the parallel writes to the WAL.&#x20;

### Transactions

RocksDB supports both optimistic and pessimistic concurrency controls. The pessimistic transactions make use of locks to provide isolation between the transactions. The default write policy in pessimistic transactions is WriteCommitted, which means that the data is written to the DB, i.e., the memtable, only after the transaction is committed. This policy simplified the implementation but came with some limitations in throughput, transaction size, and variety in supported isolation levels.&#x20;

This approach actually doesn't require any special treatment in the new spdb write flow since the txn (transaction) batches are kept in memory and are written to the WAL/memtable only as part of commit.

What is interesting for us is the WritePrepared and WriteUnprepared

In this propose we actually have for each txn:

**Prepare**

**Txn Writes**

**Commit**

In the prepare phase we write only a wal write that gets a seq as all batch writes does but this id is the identify of the txn

Any txn writes will be attached to this txn id.

The write flow will consist of a txn data structure that keeps track of the uncomplete txn.

The Txn write batches will be inserted to the memtable as the regular batch writes (can be with initial  txn id to all writes or increments one - if the second option this seq should be kept in the incomplete txn data)

And will be updated in the wal.&#x20;

The version seq will be update as well.&#x20;

Any read from the txn will try find first in the txn data and then from the memtable and  levels .

New regular writes will proceed with no effect

Read from the db and not txn read will skip incomplete txn writes, by checking if the memtable read result is part of the txn WF data.

When txn is complete a commit write executed and the relevant txn id is being removed from the txn WF data and the txn data is part of the DB and is accessible  to all.

This approach is simplify the rollback since any read will ignore the txn data,&#x20;

Memtable switch will be made ONLY when no open txn. As part of flush, if we have failed txn , the data is ignored and not part of the SST.

<br>

### Error handling

Because we wrote to memtable while writing to the wal, we might fail on part of the memtable writes/wal write, but the memtable already consists of the write. In such case, as like txn pending data, we keep a failed sequence, and will be ignored when read/flush.

<br>

## Limitations and known issues&#x20;

In the 2.4 release, the write flow feature is experimental. It currently consumes slightly more memory than usual and this will be fixed in the next release.&#x20;

## Test Results&#x20;

A comparison of RocksDB 7.7, Speedb 2.3 and Speedb 2.4 +write flow  was performed.

This was tested with db\_bench, using the configuration below:&#x20;

* Number of objects: 1Billion
* Value size: 64b
* Write buffer size: 268MB
* Number of Threads: 50
* Number of CPU cores: 16<br>

The graph illustrates the dramatic increase in writing performance when using small objects: <br>

<figure><img src="/files/VKkdwvQkpFbHJWP1RJf2" alt=""><figcaption></figcaption></figure>

<br>


# Global Delayed write

This feature improves write stabilization in multiple databases/CFs environment.

## Overview

The usability and management of multiple databases can be significantly improved with the implementation of the Global Delayed Write feature. \
The Global delayed write feature is designed to address a problem where users running multiple databases on the same disk can experience disk bandwidth starvation, leading to uneven resource distribution. This feature converts the WriteController from a per-database object into a shared object that can record and enforce the live delay requirements of all the databases that are connected to it. In this document, we'll explain the problem, the solution, and how to use the global delayed write feature.

This feature was introduced with Speedb open source version 2.4.0.&#x20;

## Problem description:

When multiple databases are running on the same disk, some databases may experience disk bandwidth starvation. The starvation is caused by the following scenario:

Db1 enters a slowdown/stop condition because the disk can't keep up with the ingest rate.&#x20;

Other dbs using the same disk are running without delay (since they themselves are not in a delay state) and they are taking up all the disk bandwidth.

In order for Db1 to exit the slowdown/stop condition, it needs to run compactions but the disk bandwidth for these compactions is unavailable due to the other dbs which leaves Db1 in a delayed state indefinitely or until the other dbs release some disk bandwidth.

## Solution Description

The global delayed write feature addresses this problem by converting the WriteController from a per-database object into a shared object. This shared object records and enforces the live delay requirements of all the databases that are connected to it. It works by enforcing the most extreme delay (i.e., the minimum rate) on all databases.

In practice, the WriteController is now a shared\_ptr option in ImmutableDBOptions. It keeps track of and enforces all the delay requirements of the column families (cfs) in the databases to which those options were passed. The default behavior of having one WriteController per database will remain, but users can also set a WriteController in the options. Note that setting a WriteController in the options is only valid when use\_dynamic\_delay = true.

## How to Use the Global Delayed Write?&#x20;

To use the global delayed write  feature, users should set use\_dynamic\_delay = true in the options. By default, each database will have one WriteController, but users can set a WriteController in the options if desired..

### Example code:

```cpp
Options options;
options.use_dynamic_delay = true
options.write_controller.reset(new WriteController(
      options.use_dynamic_delay, options.delayed_write_rate));
  DB* db1 = nullptr;
  DB* db2 = nullptr;
  Status s = DB::Open(options, "db_path1", &db1);
  Status s2 = DB::Open(options, "db_path2", &db2);
```

&#x20;

## Test Results

The new global delayed write was tested with db\_bench, using the command below.

db\_bench --compression\_type=None -db=/data/ -num=200000000 -value\_size=1000 -key\_size=16 --delayed\_write\_rate=536870912 -report\_interval\_seconds=1 -max\_write\_buffer\_number=4 -num\_column\_families=3 -histogram -max\_background\_compactions=8 -cache\_size=8388608 -max\_background\_flushes=4 -bloom\_bits=10 -benchmark\_read\_rate\_limit=0 -benchmark\_write\_rate\_limit=0 -report\_file=fillrandom.csv --disable\_wal=true --benchmarks=fillrandom,levelstats --column\_family\_distribution=50,24,26 --level0\_slowdown\_writes\_trigger=8 -write\_buffer\_size=268435456

The graph shows that with the new global delayed write enabled the results are more stable by 30%.<br>

Without the global delayed write, there are many stalls observed during the test while with the feature enabled there were no stalls. <br>

<figure><img src="https://lh5.googleusercontent.com/r1p7jQy1KO6wR1g9wCsUMb-5h0C6BbpzyVXyuxZ-IoxgaX71_K8ztjXCIAyPd_zPtS_iZq_w7xKXHsoxB_xYPAZxHm9Rg4z093ULOMUI8fA2I8NUZCy3D9YNpxjsiAKqLYyL9YKp9_Tq_tY167ggoXc" alt=""><figcaption></figcaption></figure>

\
\ <br>

<br>


# Live Configuration Changes

This document describes the Live Configuration Changes functionality in Speedb introduced in v2.3.0

## Overview&#x20;

Live Configuration Changes allows a user to change the mutable options of a Speedb database without restarting the process.  Mutable options allow the user to change certain behavior without closing the database.   For example, the user may wish to enable the Blob DB or change the compression algorithms without restarting their running database.  Prior to this feature, this could be accomplished by invoking the DB::SetOptions or DB::SetDBOptions methods.  Live Configuration Changes allow such changes to be made to a running database process without any downtime and without the programmer explicitly calling these methods.<br>

## Motivation&#x20;

Without the live configuration changes feature, in order to change a mutable option, a restart is required. Since configuration changes are sometimes required unexpectedly, this feature enables the user to update the options without any downtime.

## How does it work?

Live Configuration Changes works by periodically checking to see if a Speedb Options file (the **refresh\_options\_file**) exists and, if so, updating the Options to the values contained in the refresh\_options\_file. The frequency to check for the refresh options file is controlled by the Options.refresh\_options\_sec parameter.  If greater than zero, this parameter controls how often a background task will check if the file exists (by default, the refresh\_options\_file is checked hourly).

The name of the options file is specified in the Options.refresh\_options\_file parameter.  If the parameter points to an absolute path, the full path is used.  Otherwise, the path is relative to the database directory.  If no file name is set, the “Options.new” file in the database directory is checked.

The refresh options file will be checked periodically (every **refresh\_options\_sec** seconds).  If a file is found, it is read as an OPTIONS file. The file must be a valid options file, containing at a minimum the DBOptions and ColumnFamilyOptions “default” sections (though both may be empty and contain no parameters). &#x20;

Only the parameters that you would like to be changed should be specified in the file – other parameters will retain their current value.  Only parameters that are mutable (those which can be changed via SetOptions or SetDBOptions) may be in the file; non-mutable options will cause an error to be generated. &#x20;

The “table” section of the options file is ignored.  To update mutable table options, the options should be specified as “*table\_factory.option = new\_value*”, where “option” is the name of the option (e.g. “block\_size”) being changed.  Once processed, the refresh options file will be removed.

Because the refresh options job runs in the background asynchronously, no errors are reported to the user.  The log file should be checked to see that the values were successfully read and updated.  If there are errors while performing the update, the options may be partially applied (e.g. to the DBOptions or some of the column families). &#x20;

The name of the options file and the refresh time can be changed dynamically.  If the refresh\_options\_sec=0, this feature will be disabled.  If disabled, it can only be enabled by calling SetDBOptions with a non-zero refresh\_options\_sec or restarting the database with a non-zero time.&#x20;

## How to use it?&#x20;

1. Create an options file that includes all the options that you would like to update (see example below).  Name it as set in the Options.refresh\_options\_file parameter (default Options.new)&#x20;
2. Store the file in the database directory&#x20;
3. After the defined seconds passed, the options should be updated&#x20;

Note: The default of refresh\_options\_sec is 0.&#x20;

## Example:&#x20;

The example below shows an example refresh options file updating three properties.

&#x20;       \[DBOptions]

&#x20;       max\_background\_jobs = 32

&#x20;       \[CFOptions “default”]

&#x20;       table\_factory.block\_size = 32768

&#x20;       enable\_blob\_files = true

This example updates the DBOptions property “max\_background\_jobs”, the table property “enable\_blob\_files” and the ColumnFamilyOptions property “block\_size”.&#x20;

{% hint style="info" %}
**Note:** The \[DBOptions] section is required and can be left empty if no DBOptions are being updated.  The \[CFOptions “default”] section is also required and may be empty.  If properties in other column families were being updated, they would be updated in a corresponding \[CFOptions cf\_name] section.
{% endhint %}

<br>


# Report Index Size per Column Family

This document describes the new index size reporting per column family introduced in Speedb 2.3.0.

## Overview&#x20;

Using this feature, you can see a breakdown of the  block cache size. Prior to Speedb v2.3.0, block cache size was reported for the cache without breakdown to column families.&#x20;

Speedb v2.3.0 adds granularity to the log by adding prints of the block cache breakdown of data, index and filter sizes per column family. It is also possible to query these new properties on demand.

## Motivation&#x20;

A clear understanding of the size of the index and filter held in the cache per column family can assist in re-configuring a system if the LRU cache runs out of memory. When identifying the column family with large index size, it is possible to change the block size, change the key size or the pinning configuration of this column family in order to mitigate the issue.&#x20;

## Technical details&#x20;

The feature consists of 2 aspects:

1. A new report in the log file that accompanies the existing report of the block cache entities.
2. Allowing the users to query the newly available information via the existing properties query API.

### Reporting in the Log:

The reporting about the block cache was augmented and now, per column-family, the report includes the total size of the data blocks, filter blocks, and index blocks in the block cache.

The following image shows an example of such a log printout

<figure><img src="https://lh6.googleusercontent.com/JqM4IxkO6lHqxsoW6K7_5w-Cx9VFprvnrTR6FKbOcP6u7KtLnzMojXSZl8N7-ZcL0wcSTnqWcWPHORvKiCjlfqosNPmIIgZMv6ETs1u3nka_WpbbnWgT2HgHS5kvRGeyod7MfaK1aFKHQG1VaDi8rB4" alt=""><figcaption></figcaption></figure>

The new line in the log is the 3rd one:

Block cache \[default]  DataBlock(0.00 KB) FilterBlock(288.08 KB) IndexBlock(160.16 KB)

In this case, it is for the “default” column family.

### Querying properties:

The new per-cf statistics may be queried programmatically via the existing properties querying API. A new key was defined: DB::Properties::kBlockCacheCfStats was added to support this.

The result is a map that has the following properties:

* The name of the column-family
* The block cache’s id associated with the column family (its DB actually)
* The total size in bytes used by every cache role. The supported roles are data, filter and index. For all other roles the size would always be 0.

The following code snippet (taken from the db\_block\_cache\_test.cc unit tests file demonstrates how to use this API in the context of the test:

## Example&#x20;

```cpp
void PrintCacheStats( DBImpl* db, const std::shared_ptr<Cache>& cache){
    if  (std::string(cache->Name()) != "LRUCache") {
      // The feature is only supported for LRU-Cache
      return;
    }


    auto cf_handle = db->DefaultColumnFamily();


    std::map<std::string, std::string> cf_values;
    db->GetMapProperty(cf_handle, DB::Properties::kBlockCacheCfStats, &cf_values);


    std::cout << "Block Cache Stats for the " << cf_values[BlockCacheCfStatsMapKeys::CfName()] << " column-family:\n";
    std::cout << "Cache Id:" << cf_values[BlockCacheCfStatsMapKeys::CacheId()] << '\n';
    std::cout << "Data Size:" << cf_values[BlockCacheCfStatsMapKeys::UsedBytes(CacheEntryRole::kDataBlock)] << '\n';
    std::cout << "Index Size:" << cf_values[BlockCacheCfStatsMapKeys::UsedBytes(CacheEntryRole::kIndexBlock)] << '\n';
    std::cout << "Filter Size:" << cf_values[BlockCacheCfStatsMapKeys::UsedBytes(CacheEntryRole::kFilterBlock)] << '\n';
  }
```

<br>


# Proactive Flushing

This document describes the proactive flushing mechanism introduced in Speedb v 2.2.0

## Overview

In RocksDB, memtables are flushed for a number of reasons. Typically, this occurs when the size of a memtable reaches its maximum configuration. Flushing may also be triggered by the WriteBufferManager (or WBM, see [WBM's detailed description](https://github.com/EighteenZi/rocksdb_wiki/blob/master/Write-Buffer-Manager.md)). The WBM is an optional component. A user who wishes to limit the maximum memory used by multiple databases for memtables (the Quota), will create a WBM instance and configure all associated databases to update the WBM as needed.

The WBM tracks both the total memtable memory and the total mutable memtable memory. The WBM performs the tracking through its associated databases. These databases notify their WBM when they allocate and release memtable memory, and when their memtables become immutable.

Flushing is handled passively by the WBM. The databases call the WBM’s ShouldFlush() API during their write flows. A DB that calls ShouldFlush() and receives a positive reply, triggers a flush. The WBM’s reply is based on the current mutable and immutable memory consumption, relative to the quota.

When a DB triggers a flush due to the WBM, it has to pick CF(s) to flush. In case of atomic flush, all CFs are flushed. Otherwise, the oldest CF (the one with the smallest sequence number) is selected.

## Problems with the current method

Currently, there are a few issues to address:

1. The WBM is passive. The DB checks the WBM’s state during a DB write.
2. The CFs chosen for non-atomic flushes are based solely on their "age" and are not considered based on the amount of unflushed memtable data they have accumulated.
3. There may also be problems with immutable memtables when they are not pending for flush, either because of the "min\_num\_write\_buffers\_to\_merge" setting or because they have been flushed and are not released.

&#x20;

These issues will be apparent in a multi-db scenario. There may be some databases which are very active (heavy writers) and others which are hardly active or inactive. In such a scenario, the inactive databases may consume memtable memory that will not be released (WBM flushing is a result of database writes), while the very active databases will flush very frequently, even if their memtables consume little memory. This will result in the creation of a large number of small level-0 files, as well as not free up any memory.

&#x20;

## Solution - Proactive Flushing

A proactive flushing approach is based on the principle that the WBM initiates flushes rather than waiting for its databases to query it.

As a secondary principle, CF-s are selected for flushing based on the size of their mutable memtables.

Proactive flushing is an optional new feature, introduced in release 2.2.1. Once enabled, proactive flushing will replace the existing WBM flushing mechanism. Thus, when its ShouldFlush() API is called, the WBM will always respond with 'no'.

### Usage

The feature is enabled by default. To disable it, set the initiate\_flushes parameter to false when creating the WBM.&#x20;

The following example creates a write buffer manager with proactive flushes enabled, with a maximum number of parallel flushes == 3

```cpp
size_t buffer_size = 1 << 30U;
std::shared_ptr<Cache> cache = ...;
bool initiate_flushes = true;
FlushInitiationOptions flush_initiation_options;
flush_initiation_options.max_num_parallel_flushes = 3U;
  
Options options;

// Options initialization code
  .
  .
  .

options.write_buffer_manager.reset(new WriteBufferManager(buffer_size,
                                                            cache,
                                                            allow_wbm_stalls,
                                                            initiate_wbm_flushes,
                                                            flush_initiation_options));

  .
  .
  .

// Open the database using options
```

### Registration with the WBM

In order for the WBM to initiate flushes, every DB will register with its WBM a callback function which the WBM may use to request the DB to initiate a flush.

### Tracking the number of flushes in progress

The WBM will now track the number of flushes that are running simultaneously across all of its databases. These flushes may be those initiated by the WBM or those triggered due to other reasons (e.g., memtable size limit, manual flush, etc.).

### Adapting the number of flushes

When using proactive flushing, the user sets a maximum number of flushes that may run simultaneously in all of the WBM’s databases together as demonstrated in the example above. The WBM attempts to maintain a number of simultaneous flushes that correspond to the percentage of used memory out of its quota, up to the maximum configured number.&#x20;

### Initiating a flush

The WBM has a new internal thread that will wake up whenever it needs to initiate new flushes. The thread will iterate over its DBs, requesting each DB in turn to initiate a flush. The WBM will also specify a minimum size for the flush, to reduce the chances of generating many small level-0 files. A DB will evaluate the request and may or may not initiate a flush. It will return the result (flush initiated or not) to the WBM.

The WBM will stop requesting DB-s when one of the following occurs:

* The desired number of flushes was reached; Or
* All of the DB-s were requested to flush.

### Picking a CF

When a DB receives the WBM’s request to flush, it will evaluate the request and see if there is a CF that may be flushed. The DB will select the oldest CF that meets the minimum size requirement. However, to avoid a case where there are multiple CF-s in a DB, some heavily active (writing) and some not at all, a CF that was not picked multiple times will be picked occasionally, regardless of the minimum size requirement.

### Performance results

The new proactive flushing algorithm was tested with db\_bench, using the configuration below.

**Test description :**

* Set the write buffer manager with size of 110,000,000
* Create 3 databases.
* Performing fillseq operations on 430,000 keys with a value size of 76B on 2 databases. This has resulted in the memtable becoming mostly full but not enough to require a flush.
* Writing 100000000 keys of the same size to the last DB.

**HW configuration:**&#x20;

16 CPU cores, Intel(R) Xeon(R) Platinum 8370C CPU @ 2.80GHz

128GB memory

**Results:**

Below are benchmark results comparing the performance of Speedb v2.2.0 with proactive flushing enabled against Speedb without proactive flushing enabled.&#x20;

Based on the graphs, it can be seen that without the proactive flushing, there were many small flushes, high write amplification, and a reduction in performance

<figure><img src="/files/6lPX7ftjXfUZOSFRb8Ck" alt=""><figcaption></figcaption></figure>


# Sorted Hash Memtable

A new memtable type that improves seek and read while writing performance.

### Overview

There are currently 5 [RocksDB memtable ](https://github.com/facebook/rocksdb/wiki/MemTable)representation options, with the default being ***SkipList***.&#x20;

We designed a new memtable representation algorithm that allows parallel writes without requiring the insertion of synchronization locks, also improving read and seek operations.<br>

This new memtable is available in version 2.0.0 and later.&#x20;

### Algorithm

The Speedb memtable combines hash and sorted vectors (which are created in background thread).

The default hash size is 1 Million buckets, and the maximum size for each vector is 10K keys. (this can be tuned as part of column family creation properties).

The goal of the hash is to achieve direct access on read and write operations **(O(1))**.&#x20;

The goal of the sorted vector is to improve seek operations, taking into consideration that writes are still progressing.&#x20;

A new write is added to the hash and atomic write list.

### Write Operation

#### **Hash insertion:**

Since the default hash size is 1 Million buckets, the possibility of more than 1 write on the same bucket is reduced (not including an overwrite operation). This gives us direct access without synchronization.

#### Write list insertion:

Elements are inserted into the write list atomically (the anchor pointer is an atomic exchange). The write list is not sorted - this occurs in the background.

#### Sorted thread:

In the background, the sorted thread divides the write list into sorted vectors if at least one of the following conditions occurs:

1. The vector size limit is reached (default 10,000)&#x20;
2. A seek operation occurs

The vector will only be valid if it is sorted.

### Read Operation:

Read is done by direct access to the match memtable hash bucket. No synchronized lock is required.

### Seek Operation:

The seek request signals the sorted thread to divide a new sorted vector by adding a special entry to the write list.&#x20;

{% hint style="info" %}
Note: New writes are not suspended!
{% endhint %}

The seek request waits for the cutoff vector to be sorted. Each seek iterator has its own binary heap of shared sorted vectors relevant to the seek time creation, so there's no dependency on other iterators' progress.

{% hint style="info" %}
If no write occurs, the last sorted vector is used (a new one isn't created).&#x20;
{% endhint %}

#### Merge sorted vectors:

When there are many seek operations, a situation could arise with many small sorted vectors.&#x20;

In this case, the sorted thread's responsibility is to merge small continuous sorted vectors, so the seek iterator will be created from a small number of sorted vectors.

### Preparing the memtable&#x20;

As mentioned above, the creation of this memtable is expensive. As such, we've created a CF background thread that creates a standby memtable, which allows us to switch memtables without wasting time.&#x20;

### Advantages

1. High performance in a heavy write workload &#x20;
2. A write request during a seek request is not blocked.&#x20;
3. Sorted vectors are used on each seek operation without being copied
4. If no write occurs between two seek operations, both seek iterators will have the same sorted vector&#x20;

### Disadvantages

1. The memtable constructor size is about 8mg (depends on the bucket size)&#x20;
2. memtable creation is expensive (creating 1000000 buckets takes time)

### Performance Results&#x20;

The new sorted hash memtable was tested with db\_bench, using the configuration below.&#x20;

The following benchmark results compare the performance of Rocksdb v7.2.2 with skiplist compared to Speedb with the new sorted hash memtable code.<br>

Note: More details about how we test performance you can read in the Performance Testing chapter.&#x20;

Configuration:

* Number of objects: 1Billion&#x20;
* Value size: 1KB
* Write buffer size 64MB (default)
* Number of Threads: 4
* Max number of write buffers: 4
* Number of CF: 1 (default)
* Number of CPU cores: 16
* Compression mode: none

&#x20;**\*Up to 155% improvement in overwrite workload**&#x20;

<figure><img src="/files/llECm4LbOvGTd48gRqZ9" alt=""><figcaption><p>100% Overwrite workload <br></p></figcaption></figure>

**Up to 68% improvement in 50% random read workload**&#x20;

<figure><img src="/files/flrwByeJG7eINqRDQDgk" alt=""><figcaption><p>50% Random Read performance results</p></figcaption></figure>

**Up to 15% improvement in random seek workload**&#x20;

<figure><img src="/files/3mLbrbpwX7gvdkFD4T4U" alt=""><figcaption><p>100% random seek workload </p></figcaption></figure>

### Usage

#### db\_bench/db\_stress

./db\_bench--memtablerep=speedb.HashSpdRepFactory\
It will use the default bucket size = 1000000 In order to change the default bucket size, use the following syntax: ./db\_bench --memtablerep=speedb.HashSpdRepFactory:1000 (where 1000 is the bucket size)

#### Configuring in the user’s application code

This should be set in the DB Options object:&#x20;

```
Options options;
ConfigOptions config_options;
config_options.ignore_unknown_options = false;
config_options.ignore_unsupported_options = false;
Status s = MemTableRepFactory::CreateFromString(config_options, "speedb.HashSpdRepFactory", &options.memtable_factory);
assert(s.ok());

```


# Paired Bloom Filter

This document described how Speedb improved memory consumption while keeping the same false positive rate.

The Paired Bloom Filter consumes less memory than FastLocalBloomFilterPolicy, RockDB’s default cache-local bloom filter, without increasing the rate of false positives.

The Paired Bloom filter algorithm introduced in Speedb release 2.1.0.&#x20;

## Overview

### **RocksDB’s default bloom filter**

Although RocksDB’s default cache-local bloom filter (FastLocalBloomFilterPolicy) offers faster CPU consumption than the theoretical standard bloom filter, its rate for false positives is significantly worse.

This trade-off deteriorates the performance/memory footprint even further as the amount of bit per key increases (typically from \~20 bits per key and up). This is especially problematic in use cases where high accuracy from the filter is required.

Moreso, once very high bits-per-key levels are reached, the rate for false positives can be extremely high.

### **Speedb’s Paired Bloom Filter**

Speedb’s goal was to ensure fast and low consumption but *without sacrificing accuracy*, offering a better trade-off in terms of memory footprint vs. the rate of false positives.

**The result:** In use cases where high accuracy is required, Speedb’s Paired Bloom Filter consumes 30% less memory, without an increase in the false-positive rate.

{% hint style="info" %}
The construction and use of the Paired Bloom Filter is \~2x slower than the default bloom filter. In most use cases the performance degradation will be insignificant. However, this should be reduced in the next version due to optimization and additional improvements.

**With versions older than 2.2.0, we recommend using the Paired Bloom Filter only for bits per key rates greater than 10. As of version 2.2.0 and later, this feature is available with any bits per key value.**&#x20;

The current Paired Bloom Filter is suitable when all files contain a sufficient number of keys. The typical average number of keys per file should be above 50k keys. A lower number of keys will lead to a degradation in performance.
{% endhint %}

## Related algorithms

### **Standard Bloom Filter**

The standard bloom filter is used to check whether a certain key belongs to a predefined group of ***keys*** ***S*** of size ***n***.&#x20;

The standard bloom filter is a vector of ***m*** ***bits*** (denoted here as ***V***), which are initially set to zero.&#x20;

Insertion of *key **e*** in is done by mapping the key to ***k*** locations in the bloom filter using *k* independent hash functions (h\_*1 (e),...h\_*&#x6B;(e) ).&#x20;

The bits in the corresponding locations are then set to 1.&#x20;

The process is repeated for all keys in **V**.&#x20;

A ***key e*** belongs to ***S*** only if the bits ***V(h\_i (e))*** are 1 for **i=1..k**&#x20;

Therefore, in order to perform a query to check whether a key belongs to ***S,*** you need to map the key to ***k*** locations in the bloom filter using the ***k*** independent hash functions, and check the bits in the bloom filter (**V**).&#x20;

If one of the bits is 0, the queried key is NOT in ***S***. If all the bits are 1, the queried key might belong to ***S***, but false positives may occur.&#x20;

The bits per key rate is defined as: **c = m/n**, and controls the false positive rate (FPR); The larger the bits per key, the lower the FPR. Hence, there is a trade-off between FPR and the memory footprint of the bloom filter.&#x20;

This is the optimal case:&#x20;

**k = ln(2)\*m/n** and\
**FPR = (½)^k**

### **Blocked Bloom Filter**

The Standard Bloom Filter (above) has the specific drawback of memory non-locality when performing a query.&#x20;

This is due to the query process of a *key **e***, which includes accessing ***V*** in the locations ***h\_i (e) (i=1..k)*** producing on average 2 cache misses for negative keys and **k** cache misses for positive keys.&#x20;

In order to make the query process cache efficient, the Blocked Bloom Filter was proposed. In this filter, the elements are first mapped to a certain *block **V*** out of blocks using a hash function ***h\_0 (e)***.&#x20;

Each block is of the size of a cache line (512 bits) and is initialized to 0.&#x20;

After selecting the relevant *block **V\_l*** , an element is inserted by mapping it to ***k*** locations within the block and setting them to 1, similar to the standard bloom filter.&#x20;

To test whether an ***element*** is in ***S***, the element is first mapped to a block by using the hash *function **h\_0***, then mapped to locations within the block using the ***k*** independent hash functions. Finally, the bits in the corresponding locations are verified to all be 1 (similar to the standard bloom filter).&#x20;

The blocked bloom filter produces 1 cache miss for both negative and positive keys. However, the FPR of the blocked bloom filter is substantially worse than the FPR of the standard bloom filter for large bits-per-key rates (typically larger than 20).&#x20;

For small bits-per-key the blocked bloom filter and standard bloom filter have very similar FPR-s.&#x20;

For a larger bits-per-key rate the FPR of the blocked bloom filter may be several orders of magnitude worse than the standard bloom filter (Figure 1). The reason for this phenomenon is that the number of elements mapped for each block varies significantly across blocks.&#x20;

The distribution for the number of elements in a block follows a Binomial distribution.&#x20;

The variability in the number of elements per block gives rise to two effects:

1. The FPR changes for each block due to different bits-per-key rate, and since the FPR is exponential with the bits-per-key, overloaded blocks deteriorate the total FPR more than underloaded blocks improve it on average.&#x20;
2. The number of hash functions ***k*** is optimal only for a small fraction of the blocks.

The Blocked bloom filter is the algorithm implemented in RocksDB.

![](/files/f3O003p9X9yb75QBEFqd)

## **How to build the Paired Bloom Filter**

As described above, to tackle the performance deterioration of the Blocked Bloom filter for static databases, we propose the Paired Bloom Filter.

These are the steps required for building the filter:

1. Determine the number of blocks needed for the filter
2. Build a histogram of the number of keys mapped per block: The basic idea is to first build a histogram of the number of keys that are mapped to each block by applying **h\_0(e)** on all keys in S.
3. Partition the blocks into batches of consecutive blocks: \
   We partition the sequence of blocks to ***L*** consecutive batches, each including 128 consecutive blocks (cache-lines). Currently, the batch size is 128 and the number of blocks is always a multiple of the number of blocks in a batch.&#x20;
4. Within each batch: \
   a) Sort the blocks according to the number of keys mapped to every block in the batch \
   b) Pair the sorted blocks based on their number of mapped keys: \
   \- The first block (smallest number of mapped keys) is paired with the last block (largest number of mapped keys), the second block is paired with the second to last, etc. This ensures an extremely narrow distribution of the total number of mapped keys of the pairs, balancing each other with respect to the number of mapped keys.&#x20;
5. Build the bloom bits in the blocks by pairs: \
   Map the keys to their blocks - each key should be mapped both to its block and to the paired block. However, we use only half of the hash functions in one block and the other half in the paired block. **We arbitrarily** decide that the first half of hash functions ***h\_1 (e),... h\_k/2 (e)*** is used in the block with the smaller index (within the batch), while the other half is used in the block with larger index (we currently require k to be even). \
   The bits in the corresponding locations in each block are set to ***1***, and the process is repeated for all keys in S. \
   We save the paired block location within the batch in the first 7 bits of the block (in the case of a 128 batch, the 7 bits represent a number between 0 and 127. In the case of a different number of blocks in a batch, the number of bits will change accordingly). This leaves 505 bits in each block for the bloom bits.

### Filter building pseudo-code

#### Constants & parameters

* Batch-Size = 128&#x20;
* L = Number of Batches&#x20;
* N = Number of Blocks (= 128 \* L)
* K = Number of Hash Functions (Probes) \[Even]

#### Data structures

* Batch-Histogram: Array\[128] of integers - Initially all 0
* Histogram: Array\[L] of Batch-Histogram elements
* Batch-Pairing-Table: Array\[128] containing:
  * The index of the pair in the batch
  * Hash set indicator - 0 (Use \[h*1 (e),... hk/2 (e)]) / 1 (Use \[hk/2+1 (e),... h\_k* (e)])&#x20;
* Pairing-Table: Array\[L] of Batch-Pairing-Table
* Block: A sequence of 512 bits (initially all 0) partitioned into:
  * 7 bits: Index in batch of the pair block (0 - 127)
  * 505 bits: Bloom filter bits
* Hash sets:
  * h\_0 (e): Maps a key to its block (0 - \[N-1]) 0
  * &#x68;*\_1 (e),... h\_*&#x6B;/2 (e): Hash set 0 - Determine which bits to set in the primary block 1 k/2
  * h*k/2+1 (e),... h\_k* (e): Hash set 1 - Determine which bits to set in the secondary block

#### Algorithm

Build the histogram:

```
 For each key e in S 
  ++Histogram[h0(e)] 
 End
```

Pair blocks in the batches:

```
For each batch i from 0 to L-1 
  Sort Histogram[i] 
   Pair corresponding blocks of Pairing-Table[i] as described above 
End
```

Build the block filters:

```
For each key e in S
 Use h0(e) to calculate i and map e to its Block[i] 
 Find the block’s pair index (j) via Pairing-Table[i][j] 
 Set the first 7 bits in Block[i] to j (the pair) 
 Use Hash-Set 0 to set k/2 bits in Block[i] 
 Set the first 7 bits in Block[j] to i (the pair) 
 Use Hash-Set 1 to set k/2 bits in Block[j] 
End
```

Filter testing pseudo-code:

To test whether *key **e*** is positive or negative, apply the following algorithm:

```
Use h0(e) to calculate i and map e to its Primary Block[i] Extract the index of the pair (j)
Determine the Primary Hash Set (0 / 1): Hash Set = (i < i)? 0 : 1 The Secondary Hash Set is: 1 - Primary Hash Set 
For all k/2 hashes h in the Primary Hash Set 
 If the bit resulting from h(e) is not set in Block[i] 
 Return NEGATIVE 
End

// Repeat for Block[j] 
For all k/2 hashes h in the Secondary Hash Set 
 If the bit resulting from h(e) is not set in Block[j] 
 Return NEGATIVE 
End

Return POSITIVE
```

#### Results (#Hash Functions=16, bits-per-key=23.2-23.4):

**The algorithm was implemented in 4 environments:** \
1\. Simulation\
2\. Clean CPP code\
3\. Speedb POC: New filter type as part of the Speedb repo\
4\. RocksDB 7 / OSS repo POC&#x20;

In all 4 cases, with 23.4 bits-pre-key, the new filter achieved an FPR of \~1/55,000, which is close to the theoretical limit of a standard bloom filter.&#x20;

### Optimal use case/workload for the paired filter

1. The proposed filter should be most beneficial when there is a need for a very small FPR. This typically occurs when the penalty of a false positive is very big compared to the filter test time (database on the disk), and when true positives are rare. Typically, a true positive rate of 1/1000 will require an FPR of <1/100,000. When true positives aren't rare, a higher FPR can be used, and the benefit of the new algorithm compared to the blocked bloom filter will be less conspicuous.
2. A common real-world example of the optimal use case is a DB that performs multiple insertions into RocksDB. This type of DB often issues a Get query before every insertion, since it must verify that the inserted key doesn't already exist in RocksDB.

### **Comparison with the Ribbon filter**

Even with our improvement&#x73;**,** the Ribbon filter is superior in terms of memory-FPR trade-off by \~40%. However, the time to test a key and build the filter is expected to be 4-6 times slower than our algorithm. This means that Speedb's new algorithm offers superior results when memory is sufficient and high-speed performance is the crucial requirement.

### Forecasts and estimations

#### **1. Performance**

a) Filter building:&#x20;

It should take \~2 times to build this filter compared to the current filter. However, it might have a minor negative impact on the total flush/compaction time.

b) Filter use:&#x20;

i) For Positive queries, there's an additional cache miss, however, this cache miss should be negligible in the overall flow of a positive query. There should be a total degradation of no more than 2% in point queries iops/sec.

ii) For negative queries we expect the influence on performance to be negligible.

**Memory footprint**

1. The proposed filter with 23.4 BPK is equivalent (FPR-wise) to 28 BPK in the RocksDB’s bloom => \~16% savings.
2. Potentially, the new algorithm may improve the memory footprint by 30% for FPR of 1/1,000,000, and even more when lower FPRs are needed.

### Usage

There are 4 cases in which a user may wish to customize the type of filter used:

1. filter\_bench
2. db\_bench
3. db\_stress
4. Configuring the filter in the user’s application code

The new filter policy type is a RocksDB plug-in. To use it, you must specify its - ***speedb.PairedBloomFilter*** - name in the plug-in mechanism or create it directly.&#x20;

{% hint style="info" %}
The above string is case-sensitive, and must be used exactly as spelled.
{% endhint %}

As with other types of bloom filter policy, the user needs to configure the number of bits-per-key to use. Depending on the case, that number is either mandatory or optional. When optional and not specified, a default will be used.&#x20;

{% hint style="info" %}
The bits-per-key is a floating-point number (double).
{% endhint %}

#### Filter bench

./filter\_bench -impl=speedb.PairedBloomFilter \[-bits\_per\_key=] \[-average\_keys\_per\_filter=] \[-allow\_bad\_fp\_rate]

Default bits-per-key: 10

Default average-keys-per\_filter: 10000

Default allow-bad-fp-rate: false (don’t allow)

**Note:** As explained in section XXXXXXX (please refer to the appropriate section), using these defaults results in an effective FP rate that is intolerable, and will result in the following assertion:

*util/filter\_bench.cc: 472: Assertion prelim\_rate < tolerable\_rate failed*

This indicates that the benchmark should not be run under this effective FP rate.

To allow filter\_bench to run, you should do one or more of the following:

* Increase the bits per key
* Increase the average keys per filter&#x20;
* Allow bad FP rate

#### db\_bench

./db\_bench -filter\_uri=speedb.PairedBloomFilter:23.4

The bits-per-key value is mandatory in this case. Failure to specify this number will result in the following error message:

*failure creating filter policy\[speedb.PairedBloomFilter]: Not implemented: Could not load FilterPolicy: speedb.PairedBloomFilter*

#### db\_stress

./db\_stress -filter\_uri=speedb.PairedBloomFilter \[-bloom\_bits=]&#x20;

Default bits-per-key: 10

#### Configuring the filter in the user’s application code

Include the filter policy header:&#x20;

*#include "rocksdb/filter\_policy.h"*

Set the filter in the table options:

```
Options options;
BlockBasedTableOptions bbto;
ConfigOptions config_options;
config_options.ignore_unknown_options = false;
config_options.ignore_unsupported_options = false;
Status s = FilterPolicy::CreateFromString(config_options, "speedb.PairedBloomFilter:23.2", &bbto.filter_policy);
assert(s.ok());
Options.table_factory = NewBlockBasedTableFactory(bbto);

```

You may *view* an example in the file called "speedb\_db\_bloom\_filter\_test.cc" (plugin/speedb/paired\_filter/speedb\_db\_bloom\_filter\_test.cc).

### Testing&#x20;

#### Unit testing&#x20;

Add applicable unit testing for the following aspects:&#x20;

1. Customizable support: Tests like those in customizable\_test.cc&#x20;
2. Functional tests: Tests like those in full\_filter\_block\_test.cc&#x20;
3. FPR Test: A test of the FPR of the filter under certain conditions (mainly the bits-per-key). By default, this will test on a relatively small number of keys to reduce the time required to complete the test.&#x20;

#### Performance testing&#x20;

**Optimal use-case**

To test the optimal scenario, the following is recommended:&#x20;

1. Random fill of a large number N of keys (eg., N = 100 Million) followed by \
   Read random of 100 X N keys (eg., 10 Billion)&#x20;
2. Run this scenario 3 times: \
   New paired block bloom filter, BPK = 23.4 \
   Default RocksDB Blocked bloom filter, BPK = 23.4 \
   Ribbon filter, BPK = 23.4&#x20;

**Additional use-cases**&#x20;

Run the usual performance tests with the default filter and the new filter. Use the default bits per key for both, then compare the results. This is to verify that the new filter doesn’t cause an unexpected performance degradation.&#x20;

### Performance benchmark

#### Test 1 - same FPR, less memory

The benchmark compares between the local bloom filter and the new paired bloom filter. \
The test was running with the following configuration:

* Number of objects: 1 Billion
* Value size: 256 bytes
* Write buffer size: 122MB (default)
* Number of threads: 4
* Max number of write buffer: 1
* Number of Column families: 1 (default)
* Number of CPU cores: 16
* Compression mode: None&#x20;

#### Results&#x20;

As compared to the traditional bloom filter, the new Paired bloom filter showed a reduction in memory consumption while keeping the same false positive rate.&#x20;

Memory usage decreases as the number of bits per key increases.&#x20;

<table><thead><tr><th width="273"></th><th>Paired Bloom</th><th>Local Bloom</th></tr></thead><tbody><tr><td>Bits per key</td><td>40</td><td>29</td></tr><tr><td>Total memory consumption </td><td>6200MB</td><td>7700MB</td></tr></tbody></table>

This test resulted in a 23% reduction in memory consumption while keeping the same performance for random read workload.&#x20;

<figure><img src="/files/36YDCIHAHqlo9zazqqHF" alt=""><figcaption><p>100% random read workload </p></figcaption></figure>

#### Test2 - same memory usage, improved FPR

As compared to the traditional bloom filter, the new Paired bloom filter in this test improved the false positive rate while using the almost the same amount of memory.&#x20;

Test configuration:

* Number of objects: 1 Billion
* Value size: 256 bytes
* Write buffer size: 256MB (default)
* Number of threads: 4
* Max number of write buffer: 1
* Number of Column families: 1 (default)
* Number of CPU cores: 8
* Compression mode: None

<table><thead><tr><th width="284"></th><th>Paired Bloom</th><th>Local Bloom</th></tr></thead><tbody><tr><td>Bits per key</td><td>20</td><td>20</td></tr><tr><td>Total memory consumption</td><td>4900B</td><td>4800MB</td></tr></tbody></table>

This test simulates high ratio of non-existing keys and the disk is the bottleneck. \
The graph below demonstrate the significant improvement with random read in this type of workload.&#x20;

<figure><img src="/files/ItEyXo915AuWYCtKQwyB" alt=""><figcaption></figcaption></figure>


# Enhancements

This section describes the improvements made by Speedb to the existing RocksDB mechanisms.


# Range Delete Improvement

This feature related to issue 363 https\://github.com/speedb-io/speedb/issues/363

## Introduction:

This feature improves the Range Delete and not flushing the deleted element to the SST. It only  keeps the deleted record and reduces SST size for later compaction.

The Range Delete improvement aims to reduce the resources consumed during the flush process of immutable memory tables. Specifically, this feature avoids writing value key records to the SST file if they have a corresponding match tombstone record and are not part of any existing snapshot. By skipping these records, we can reduce the size of the SST file and minimize the workload for future compaction.

In the old implementation, when flushing the memtable, if a key matches a key in the delete range table and there are no associated snapshots for that record, the key is still written to the SST file along with its value.

## Motivation&#x20;

When a group of immutable memory tables is flushed and a new SST file is created, a value key record is still written to the SST file even though it has a match tombstone record. This is an expensive resource that we can save. The SST file will be smaller, and later compaction will have less work to do.

## Implementation&#x20;

To implement this feature, we iterate over the immutable memtables group records during the building of the context of an SST file as part of the WriteLevel0Table function. Before adding a record to the SST write buffer, we check whether any action needs to be taken. For value key records, we check if the key is in the range of the match immutable memtables tombstone record. If the key is in the range and not part of an existing snapshot (i.e., its sequence is not greater than the snapshot sequence), we skip the value key record and keep only the tombstone record.

## How to use it?

This option is a new DB options called:

"use\_clean\_delete\_during\_flush"

\
\ <br>


# Dynamic Delayed Writes

The purpose of this document is to describe how Speedb v2.2.0 has achieved performance stabilization with its dynamic delayed write mechanism

## Overview

This feature offers an alternative method to calculate the delayed write rate using the flag - use\_dynamic\_delay. Based on the CF slowdown limits, the new calculation reduces the delayed write rate (DWR) in a linear manner.

Here is where the DWR is calculated and set:

Whenever the state of the LSM changes, which happens in every call to InstallSuperVersion, RecalculateWriteStallConditions checks the state of the CF and checks if its in a delayed state if one of the following happens:&#x20;

1. Writing to the last memtable when there's more than 3
2. L0 files > level0\_slowdown\_writes\_trigger
3. compaction pending bytes > soft\_pending\_compaction\_bytes\_limit

## Motivation

In the current calculation, the DWR is determined based on changes in compaction pending bytes between the current call to RecalculateWriteStallConditions and the previous call (which is called on every InstallSuperVersion). Whenever compaction pending bytes are less than the previous value, the current DWR is increased by 25%, while when they are equal to or greater than the previous value, the DWR is reduced by 20%.

Thus, regardless of how much the state has changed, the DWR will always change by the same amount, which results in an unjust increase or decrease in the DWR. This in turn, causes spikes in write performance as can be seen in the following analysis - <https://github.com/speedb-io/speedb/issues/154#issuecomment-1310053858> .

Furthermore, these changes are larger the closer we are to the original (user) DWR, e.g. reduce by 20Mb/s from 100Mb/s versus reduce by 2 Mb/s from 10MB/s. As a consequence, the closer we get to the stop limitation, the less we change the amount by which we delay, which is not what we want.\
We would like the delay to be small for small violations of the limitations (L0 files, compaction pending bytes, memory) and increase the delay by bigger quantities as we get closer to the stop limitations but for the meanwhile we’ll settle for equal amounts of change.

When use\_dynamic\_delay = false, the write rate is calculated in SetupDelay in db/column\_family.cc. (called from RecalculateWriteStallConditions). <br>

We propose to calculate the DWR based on how much we're currently in a violation of the limitation and calculate according to the user DWR and not the previously calculated DWR. e.g. if the user DWR is 100MB/s, L0 slowdown is set to 10 and stop is set to 20 files, and there are 14 L0 files:

There are 10 steps until the write stop. So each step toward the stop is a 10MB/s reduction from the delayed rate the user specified and the other way when increasing the write rate.

10 L0 files - 100 Mb/s

11 L0 files - 90 Mb/s

until total stop at 20 L0 files.

Same solution for compaction pending bytes.&#x20;

For memtables, set up a 10X delay once the last memtable is being written to.

The biggest violation out of all 3 cases (L0 files, pending bytes or memtables) will be the one deciding the delay factor.&#x20;

The new calculation is done in CalculateWriteDelayDivider which is called fromRecalculateWriteStallConditions through DynamicSetupDelay.<br>

### Test&#x20;

We tested the new write delay mechanism on a database with 80 million objects, while the value size is 1KB, We used 50 threads and 16 CPU cores Intel(R) Xeon(R) Platinum 8370C CPU @ 2.80GHz.

We were running a massive write workload to trigger the delayed write mechanism.&#x20;

### Test Results

Based on the graph below, we can see that the new write delay mechanism provides consistent performance compared with the previous mechanism.&#x20;

<br>

<figure><img src="/files/5OXG6DP07C1HJIiUZBwg" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/ot7OEqgpI7RfP7l1CIP2" alt=""><figcaption></figcaption></figure>


# Reduce switch memtable latency

## Motivation&#x20;

The MemTableRepFactory class (each memtable representation inherits it) can be set as a “PrepareMemtableCreation” which  will create a switch background thread that is responsible to construct a new memtable object to be used when switch occurs. A new memtable will be created when we fetch this memtable, so we'll always have one memtable available for use.

In case we don't have a ready memtable (this can happen if the switch memtable happened before the previous trigger was triggered) we create the memtable immediately .

To be able support this ability , memtable representation needs to divide the memtable construction to pre/post phases . This allows us to get the correct memtable’s  parameters as defined in the mutable CF configuration.

## Implementation  &#x20;

The MemTableRepFactory class (each memtable representation inherits it) can be set as a “PrepareMemtableCreation” which  will create a switch background thread that is responsible to construct a new memtable object to be used when switch occurs. A new memtable will be created when we fetch this memtable, so we'll always have one memtable available for use.

In case we don't have a ready memtable (this can happen if the switch memtable happened before the previous trigger was triggered) we create the memtable immediately .

To be able support this ability , memtable representation needs to divide the memtable construction to pre/post phases . This allows us to get the correct memtable’s  parameters as defined in the mutable CF configuration.<br>

## Advantage&#x20;

1 - A switch memtable (size of the write buffer, threshold of the write buffer manager, wall size etc.) does not have a memory construction penalty and will allow for fluent acceptance of new writes. &#x20;

2 - Simplified code

## Disadvantage&#x20;

1 - Each CF will have two mutable memtables (one active and one on hold), so we multiply the memtable construct memory usage.

<br>


# Tools


# Log Parser

* [Overview](#speedbslogparsertool-overview)
* [Terminology](#speedbslogparsertool-terminology)
* [Major Capabilities](#speedbslogparsertool-majorcapabilities)
* [Usage](#speedbslogparsertool-usage)
* [Installation / Getting Started / Prerequisites](#speedbslogparsertool-installation-gettingstarted-prerequisites)
* [Tool’s Outputs Description](#speedbslogparsertool-toolsoutputsdescription)
  * [Abbreviations, Conventions and Terms used in the log parser’s outputs](#speedbslogparsertool-abbreviations-conventionsandtermsusedinthelogparsersoutputs)
  * [Timestamps](#speedbslogparsertool-timestamps)
  * [Availability of Data](#speedbslogparsertool-availabilityofdata)
    * [Log level](#speedbslogparsertool-loglevel)
    * [Availability of the information to parse and process](#speedbslogparsertool-availabilityoftheinformationtoparseandprocess)
  * [Options](#speedbslogparsertool-options)
    * [DB Options and CF Options](#speedbslogparsertool-dboptionsandcfoptions)
    * [Defaults](#speedbslogparsertool-defaults)
    * [Writing the options to the log](#speedbslogparsertool-writingtheoptionstothelog)
    * [Log Rolling](#speedbslogparsertool-logrolling)
    * [Opening an existing DB (DB Recovery)](#speedbslogparsertool-openinganexistingdb-dbrecovery)
    * [Number of Column Families and its implications](#speedbslogparsertool-numberofcolumnfamiliesanditsimplications)
    * [Unavailability of CF Names in Rolled Logs (Auto-Generated CF Names)](#speedbslogparsertool-unavailabilityofcfnamesinrolledlogs-auto-generatedcfnames)
* [The Output Folder](#the-output-folder)
* [Console Output](#speedbslogparsertool-consoleoutput)
* [Console Short Output Description](#speedbslogparsertool-consoleshortoutputdescription)
  * [Example Output](#speedbslogparsertool-exampleoutput)
  * [Output Fields Description](#speedbslogparsertool-outputfieldsdescription)
* [JSON File](#speedbslogparsertool-jsonfile)
  * [Top Level JSON Objects](#speedbslogparsertool-topleveljsonobjects)
  * [JSON Top-Level Objects Detailed Description](#speedbslogparsertool-jsontop-levelobjectsdetaileddescription)
    * [General](#speedbslogparsertool-general)
    * [Options](#options)
      * Notes about the display of Options
      * Options Diff
        * Overview
        * DB Options Diff
        * CF-s Options Diff - CF-s (Common) Sub-Object
        * CF-s Options Diff - CF-s (Specific) Sub-Object
      * All Options
    * [DB-Size Sub-Object](#speedbslogparsertool-db-sizesub-object)
  * [Flushes Object](#speedbslogparsertool-flushesobject)
  * [Compactions Object](#speedbslogparsertool-compactionsobject)
  * [Reads Object](#speedbslogparsertool-readsobject)
    * Get Histogram
    * Multi-Get Histogram
    * Per CF Read Latency
    * Filter Effectiveness
  * [Seeks Object](#speedbslogparsertool-seeksobject)
  * [Warnings Object](#speedbslogparsertool-warningsobject)
  * [Block-Cache-Stats Objects](#speedbslogparsertool-block-cache-statsobjects)
    * Top-level sub-objects
    * Caches top-level sub-object
    * DB Counters top-level sub-object
    * Detailed top-level sub-object
* [CSV Files](#speedbslogparsertool-csvfiles)
  * [counters.csv](#speedbslogparsertool-counters.csv)
  * [Counters Histograms CSV-s](#speedbslogparsertool-countershistogramscsv-s)
    * histograms\_human\_readable.csv
    * histograms\_tools.csv
  * compactions\_stats.csv
  * compactions.csv
  * flushes.csv
* [Testing](#testing)
* [Miscellaneous](#speedbslogparsertool-miscellaneous)
  * Reporting Bugs and Proposing
  * Known Bugs
  * Contributing to the tool’s development
  * Getting Advice or Discussing the Tool
  * The Future (the Features)

## Overview <a href="#speedbslogparsertool-overview" id="speedbslogparsertool-overview"></a>

Speedb's Log Parser is a tool that may be used to parse and process Speedb and RocksDB log files.

The tool extracts useful information from these logs and aids users in gaining insights about their systems with ease. It is expected to be a valuable tool for novices and experts alike.

The tool is written in the Python language and consists of a set of Python scripts.

It resides in a GitHub repository (<https://github.com/speedb-io/log-parser>). There is a [README.md](http://README.md) file in the root folder of the repository that describes how to install the tool, contribute to its development, etc.

## Terminology <a href="#speedbslogparsertool-terminology" id="speedbslogparsertool-terminology"></a>

The following terms and abbreviations are used throughout this document:

·       CF: Column-Family

·       DB / DB-Wide: Applicable to the entire DB rather than to a specific CF.

## Major Capabilities <a href="#speedbslogparsertool-majorcapabilities" id="speedbslogparsertool-majorcapabilities"></a>

* Parses a single Speedb or RocksDB log file (parsing multiple logs in in the **road-map**)
* &#x20;Parses the log and processes the information about the following elements and entities (detailed description may be found in the sections below):
  * Metadata information about the instance that has generated this log file (e.g., library version generating the log) (General Section).
  * Speedb / RocksDB Options (Options Section):
    * All of the options with their values (db-wide and per column family).
    * Displays the difference between the options in the log and an applicable baseline.
  * &#x20;Data about the size of the DB (DB-Size Section).
  * Flushes and Compactions (Flushes and Compactions sections respectively)
  * Information regarding DB read operations (Reads Section).
  * Information regarding seek DB operations (Seeks Section).
  * Warnings issued by the DB (Warnings Section).
  * Block cache statistics (Block-Cache-Stats Section)
  * Various Statistics: Counters, histograms, compaction stats, etc.
* The tool generates multiple outputs. Details may be found in the sections that follow. The outputs include:
  * A short console output (the default output format)
  * A JSON file with detailed information.
  * CSV files containing information about the counters, counters histograms, compactions, and flushes statistics.
  * &#x20;A detailed console output (the JSON file printed to the console).

## Usage <a href="#speedbslogparsertool-usage" id="speedbslogparsertool-usage"></a>

```
udi@udi-speedb:~/log-parser$ python3 log_parser.py -h
usage: log_parser.py [-h] [-c {short,long}] [-j] [-o OUTPUT_FOLDER] [-l] log-file-path

positional arguments:
  log-file-path         A path to a log file to parse (default: None)

optional arguments:
  -h, --help            show this help message and exit
  -c {short,long}, --console {short,long}
                        Print to console a summary (short) or a detailed (long) output (default: None)
  -j, --generate-json   Optionally generate a JSON file in the output folder's run-folder, with detailed information. If generated, it will be called log.json.(default: False)
  -o OUTPUT_FOLDER, --output-folder OUTPUT_FOLDER
                        The name of the folder where output files will be stored in SUB-FOLDERS named run_dddd, where 'dddd' is the run number (default: output_files)
  -l, --generate-log    Generate a log file for the parser's log messages (default: False)

Notes:
- The default it to print to the console in a short format.
- It is possible to specify both json and console outputs. Both will be generated.

```

## Installation / Getting Started / Prerequisites <a href="#speedbslogparsertool-installation-gettingstarted-prerequisites" id="speedbslogparsertool-installation-gettingstarted-prerequisites"></a>

Please see the [README.md](http://README.md) file in the tool’s repository for this information.

## Tool’s Outputs Description <a href="#speedbslogparsertool-toolsoutputsdescription" id="speedbslogparsertool-toolsoutputsdescription"></a>

### Abbreviations, Conventions and Terms used in the log parser’s outputs <a href="#speedbslogparsertool-abbreviations-conventionsandtermsusedinthelogparsersoutputs" id="speedbslogparsertool-abbreviations-conventionsandtermsusedinthelogparsersoutputs"></a>

·       **tool:** The log parser

·       **Log file / log: A RocksDB / Speedb information log. Not the WAL (Write-Ahead-Log)**

·       **Parsed Log: The log file given to the log parser for parsing.**

·       **db:** Database

·       **db options**: Options that are not specific to any cf. Applicable to the entire db.

·       **cf options**: Options that are specific to an individual cf.

·       **cf (cf-s)**: Column Family (Column Families)

·       **Units:**

o   **Size Units**: B (Bytes), KB (Kilobytes), MB (Megabytes), TB (Terabytes)

o   **Numbers Units**: K (Thousands), M (Millions), G (Billions).

### Timestamps <a href="#speedbslogparsertool-timestamps" id="speedbslogparsertool-timestamps"></a>

**Log timestamps** (e.g., ‘2023/01/04-08:54:59.130735’) **are in local time**. The resolution is microseconds. All of the information displayed by the tool is using the timestamps from the parsed log as is.

### Availability of Data <a href="#speedbslogparsertool-availabilityofdata" id="speedbslogparsertool-availabilityofdata"></a>

#### Log level <a href="#speedbslogparsertool-loglevel" id="speedbslogparsertool-loglevel"></a>

The `info_log_level` db option controls the minimal level of issued log traces that will actually be written to the log (`INFO_LEVEL` by default in production library builds). Using a higher logging level will result in a log file that has almost no useful information. In that case, the tool (as well as the log file itself) will be of little use in practice.

#### Availability of the information to parse and process <a href="#speedbslogparsertool-availabilityoftheinformationtoparseandprocess" id="speedbslogparsertool-availabilityoftheinformationtoparseandprocess"></a>

The tool relies solely on the information contained in the parsed log file. Consequently, the information it displays reflects that. For example, the tool can’t display the average size of a key or a value in the entire DB, or the total number of keys in the DB, as this information is not printed to the log file.

Other common cases in which data may not be available:

·       Not having statistics (a configuration options)

·       Rolled logs (see “Log Rolling” below)

·       The number of cf-s (see “Number of Column Families and its implications” section below)

·       Lack of applicable activity. For example, no flush or compaction for a cf.

When data is not available, the output of the log parser will reflect that. For example:

·       “Filter Data Not Available”: When there is no information about the filter policy that was configured for a cf.

·       "Data Unavailable (No Stats Available)": When the DB was configured not to use statistics.

In the sections that follow, where applicable, there is a description of the information elements in the log file that were used to generate the associated output. For example:

·       Flush events and associated flush log traces

·       DB Statistics Dump

·       Counters and Histogram dumps

### Options <a href="#speedbslogparsertool-options" id="speedbslogparsertool-options"></a>

#### DB Options and CF Options <a href="#speedbslogparsertool-dboptionsandcfoptions" id="speedbslogparsertool-dboptionsandcfoptions"></a>

There are options that may be configured individually for every cf. These are called **cf options**. Options that are not specific to a cf are called **DB (or db-wide) options**. DB Options apply to the entire DB.

#### Defaults <a href="#speedbslogparsertool-defaults" id="speedbslogparsertool-defaults"></a>

Speedb and RocksDB come with defaults for every option. A user may override any of these options when opening a db. A user may also override any of the cf options when creating a new cf explicitly.

#### Writing the options to the log <a href="#speedbslogparsertool-writingtheoptionstothelog" id="speedbslogparsertool-writingtheoptionstothelog"></a>

The options are written to the log in multiple cases:

·       When a db is opened, the db and cf options are written to the log. This may occur when opening a new DB, or when recovering an existing db (with its existing cf-s and potentially newly created cf-s) from persistent storage.

·       When a new cf is created, its options are written to the log.

·       When a log is rolled, the db and cf options are written to the new log.

However, please see below for important information regarding applicable limitations.

#### Log Rolling <a href="#speedbslogparsertool-logrolling" id="speedbslogparsertool-logrolling"></a>

Log rolling (also called log rotation) is the process used to stop writing to an active log file, renaming it, and opening a new log file to which logging will be directed until the time comes to roll/rotate the log again. There are a few options that control this mechanism. In this document, log files that were rolled, are called **rolled logs**.

At the time of this writing, the defaults are to use a single log file (`max_log_file_size` option), effectively avoiding log rolling altogether. Some more information may be found [here](https://github.com/facebook/rocksdb/wiki/Logger).

#### Opening an existing DB (DB Recovery) <a href="#speedbslogparsertool-openinganexistingdb-dbrecovery" id="speedbslogparsertool-openinganexistingdb-dbrecovery"></a>

When the DB is opened as part of db recovery (opening an existing db), a new log file is created. This is unrelated to log rolling described above, but results in multiple log files nevertheless.

#### Number of Column Families and its implications <a href="#speedbslogparsertool-numberofcolumnfamiliesanditsimplications" id="speedbslogparsertool-numberofcolumnfamiliesanditsimplications"></a>

A user may create any number of column families (cf-s).

However, while opening a DB, **only the options of the first 10 (hard coded) cf-s will be printed to the log**.

The log will contain the following information for the 11th cf onwards:

{% code fullWidth="true" %}

```
2023/06/06-12:56:30.438743 322453 [/db_impl/db_impl.cc:3317] Created column family [column_family_name_000009] (ID 9)
2023/06/06-12:56:30.806215 322453 [/column_family.cc:625] 	(skipping printing options)
2023/06/06-12:56:30.806365 322453 [/db_impl/db_impl.cc:3317] Created column family [column_family_name_000010] (ID 10)
2023/06/06-12:56:31.191732 322453 [/column_family.cc:625] 	(skipping printing options)
2023/06/06-12:56:31.191836 322453 [/db_impl/db_impl.cc:3317] Created column family [column_family_name_000011] (ID 11)
2023/06/06-12:56:31.635102 322453 [/column_family.cc:625] 	(skipping printing options)
2023/06/06-12:56:31.635291 322453 [/db_impl/db_impl.cc:3317] Created column family [column_family_name_000012] (ID 12)
2023/06/06-12:56:32.053358 322453 [/column_family.cc:625] 	(skipping printing options)
2023/06/06-12:56:32.053527 322453 [/db_impl/db_impl.cc:3317] Created column family [column_family_name_000013] (ID 13)
2023/06/06-12:56:32.508987 322453 [/column_family.cc:625] 	(skipping printing options)
2023/06/06-12:56:32.509108 322453 [/db_impl/db_impl.cc:3317] Created column family [column_family_name_000014] (ID 14)

```

{% endcode %}

**So, we know that there are additional cf-s, and we know their names and id-s, but we do not know their options.**

#### Unavailability of CF Names in Rolled Logs (Auto-Generated CF Names) <a href="#speedbslogparsertool-unavailabilityofcfnamesinrolledlogs-auto-generatedcfnames" id="speedbslogparsertool-unavailabilityofcfnamesinrolledlogs-auto-generatedcfnames"></a>

When a new DB is opened, or when a cf is created explicitly (via the `CreateColumnFamily` or `CreateColumnFamilies` APIs) , this is what the log contains (Only the first lines are shows):

{% code fullWidth="true" %}

```
2023/06/06-12:56:28.376394 322453 [/column_family.cc:620] --------------- Options for column family [default]:
2023/06/06-12:56:28.376399 322453               Options.comparator: leveldb.BytewiseComparator
2023/06/06-12:56:28.376400 322453           Options.merge_operator: 0x7fdfd8068640
2023/06/06-12:56:28.376402 322453        Options.compaction_filter: None
2023/06/06-12:56:28.376403 322453        Options.compaction_filter_factory: None

```

{% endcode %}

**The first line contains the name of the cf (“default” in this case)**

However, when a new log file is created as part of log rolling, **this first line is not printed to the log** => the tool has no (simple and safe) way of knowing to which cf the options belong. As the “default” cf always exists and is the first one, the tool assumes the first to be “default”, but not for the rest.

**In addition, there is no information about the skipped cf-s (those that are > 10, see above).**

The rest of the log contains traces that may contain the names of the cf-s, but that is of no use in the association of options with their cf-s.

The log parser handles this by **auto-generating cf names** for the cf-s whose names are unknown, but for which there are options.

**The auto generated names have the following format: UNKNOWN-CF-#\<Number>**

The following snapshot shows an example:

![](/files/8jLOGckpkufS3f16UTlt)

As a consequence of what is described above, **when the tool parses a log that is created as part of log rolling and there are more than 10 cf-s, the tool doesn’t know how many cf-s there are**.

This will be indicated to the user by displaying the following information in the console short output, or the json’s General object:

{% code lineNumbers="true" fullWidth="true" %}

```python
Num CF-s (*)             : Can't be accurately determined
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|       Column Family       |      Size        | Avg. Key Size | Avg. Value Size |    Compaction Style   | Compression |                Filter-Policy                 |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|          default          | Data Unavailable |      24 B     |      100 B      | kCompactionStyleLevel |    Snappy   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000001 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000002 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000003 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000004 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000005 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000006 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000007 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000008 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000009 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000010 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000012 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000013 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
| column_family_name_000014 | Data Unavailable |      24 B     |      100 B      |        UNKNOWN        |   UNKNOWN   | rocksdb.internal.FastLocalBloomFilter (24.0) |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

(*) Please see the 'Ability to determine the number of cf-s' section in the log parser's documentation for more information
```

{% endcode %}

<figure><img src="/files/DCxCJGdA29gl7CFxU4RC" alt=""><figcaption></figcaption></figure>

### The Output Folder

Every run of the log parser generates multiple output files. All of the files generated in a single run are placed under a single folder. The folder’s location and name are determined as follows:

·       The user may specify a parent folder via the ‘-o’ command line parameter.

·       If the user doesn’t specify an output parent folder, ‘output\_files’ will be used by default.

·       If the parent folder doesn’t exist, it will be created.

·       Under the parent folder, the parser will create a folder named ‘run\_dddd’ and place the run’s output files under that folder. ‘dddd’ are 4 digits that compose the run’s number. They are incremented every run and wrap around if reaching ‘9999’.

·       If the parent folder already contains ‘run\_dddd’ folders, the tool will detect the largest one (converting ‘dddd’ to its numeric equivalent - N), create a new folder ‘run\_mmmm’ where int('mmmm') = N+1

An example to put it all together:

The user specified ‘-o Output' as an argument when running the tool. There is no folder named ‘Output’ => The tool will create a new folder named ‘Output’, and the output files will be under ‘Output/run\_0001/’

The user re-runs the tool, again specifying ‘-o Output’:

The run’s output files will be under ‘Output/run\_0002’

### Console Output <a href="#speedbslogparsertool-consoleoutput" id="speedbslogparsertool-consoleoutput"></a>

There are 2 console output flavors:

1\.     **Short**: A concise summary of the major information elements. May be used to get a basic understanding of the db’s operation according to the parsed log.

2\.     **Detailed**: The contents of the JSON file, printed on the console (please see the JSON file description section for more details). This option is available for power users that wish to use only the console. To be of use, these users will probably use JSON command-line filtering tools such as [JQ](https://stedolan.github.io/jq/).

### Console Short Output Description <a href="#speedbslogparsertool-consoleshortoutputdescription" id="speedbslogparsertool-consoleshortoutputdescription"></a>

#### Example Output <a href="#speedbslogparsertool-exampleoutput" id="speedbslogparsertool-exampleoutput"></a>

{% code lineNumbers="true" fullWidth="true" %}

```
udi@udi-speedb:~/log-parser$ python3 log_parser.py  test/input_files/LOG_speedb 
Log file: file:///home/udi/log-parser/test/input_files/LOG_speedb
Baseline Log: file:///home/udi/log-parser/baseline_logs/LOG-speedb-2.3.0
Counters CSV Is in file:///home/udi/log-parser/output_files/run_0016/counters.csv
Human Readable Counters Histograms CSV Is in file:///home/udi/log-parser/output_files/run_0016/histograms_human_readable.csv
Compactions Stats CSV Is in file:///home/udi/log-parser/output_files/run_0016/compactions_stats.csv
Compactions CSV Is in file:///home/udi/log-parser/output_files/run_0016/compactions.csv
Flushes CSV Is in file:///home/udi/log-parser/output_files/run_0016/flushes.csv

Parsing of: /home/udi/log-parser/test/input_files/LOG_speedb
============================================================
Name                     : /home/udi/log-parser/test/input_files/LOG_speedb
Start Time               : 2023/04/09-12:27:35.901086
End Time                 : 2023/04/09-12:37:36.500378
Log Time Span            : 0d 00h 10m 00s
Creator                  : Speedb
Version                  : 2.4.0 [8ef7469a7f8d1d100a7a187b3c682a48777b7047]
DB Size (*)              : 111.6 MB
Num Keys Written         : 48.0 M
Avg. Written Key Size    : 24 B
Avg. Written Value Size  : 64 B
Num Warnings             : 0
Error Messages           : No Error Messages
Fatal Messages           : No Fatal Messages
Ingest (*)               : 3.9 GB
Ingest Rate              : 6.76 MBps
Statistics               : Available
Writes                   : 10.0% (48033559/480337639)
Reads                    : 90.0% (432304080/480337639)
Seeks                    : 0 (No Seek Operations)
Deleted (Flushed) Entries: 0 (No Delete Operations)
Num CF-s (**)            : 3
---------------------------------------------------------------------------------------------------------------------------------------
|       Column Family       | Size (*) | Avg. Key Size | Avg. Value Size |    Compaction Style   |  Compression  |   Filter-Policy    |
---------------------------------------------------------------------------------------------------------------------------------------
|          default          | 40.8 MB  |      24 B     |       64 B      | kCompactionStyleLevel | NoCompression | bloomfilter (10.0) |
| column_family_name_000001 | 38.3 MB  |      24 B     |       64 B      | kCompactionStyleLevel | NoCompression | bloomfilter (10.0) |
| column_family_name_000002 | 32.6 MB  |      24 B     |       64 B      | kCompactionStyleLevel | NoCompression | bloomfilter (10.0) |
---------------------------------------------------------------------------------------------------------------------------------------

(*) Data is calculated at: 2023/04/09-12:37:27.398344
(**) Please see the log parser's documentation for more information
```

{% endcode %}

#### Output Fields Description <a href="#speedbslogparsertool-outputfieldsdescription" id="speedbslogparsertool-outputfieldsdescription"></a>

<table data-header-hidden data-full-width="true"><thead><tr><th width="79"></th><th></th><th></th><th></th></tr></thead><tbody><tr><td></td><td><strong>Field Name</strong></td><td><strong>Meaning</strong></td><td><strong>Comments</strong></td></tr><tr><td>1</td><td>Title (“Parsing of:…”)</td><td>Parsed Log full path</td><td></td></tr><tr><td>2</td><td>Name</td><td>Parsed Log full path</td><td></td></tr><tr><td>3</td><td>Start Time</td><td>Time of first log entry</td><td></td></tr><tr><td>4</td><td>End Time</td><td>Time of last log entry</td><td></td></tr><tr><td>5</td><td>Log Time Span</td><td>The time difference between the Start Time and the End Time</td><td>The value is expressed in number of days, hours, minutes, and seconds.</td></tr><tr><td>6</td><td>Creator</td><td>The creator of the library that generated the parsed log</td><td>Currently may be either Speedb or RocksDB</td></tr><tr><td>7</td><td>Version</td><td>The library’s version [Git Hash]</td><td></td></tr><tr><td>8</td><td>DB Size (*)</td><td>The total size of all of the SST files in the database</td><td>The point in time in which the value was calculated is given below the per-cf table: “(*) Data is calculated ….”</td></tr><tr><td>9</td><td>Num Keys Written</td><td>The total number of keys written to the DB</td><td><p>The value of the <code>rocksdb.number.keys.written</code> counter if available,</p><p>otherwise, extracted from cumulative writes information (DB Stats)</p><p>If none of the above is available: “Data Unavailable”</p></td></tr><tr><td>10</td><td>Avg. Written Key Size</td><td>The average size of a written key</td><td><p>Calculated from information in <code>table_file_creation</code> events</p><p>If there are no such events, “Data Unavailable”</p></td></tr><tr><td>11</td><td>Avg. Written Value Size</td><td>The average size of a written value</td><td><p>Calculated from information in <code>table_file_creation</code> events</p><p>If there are no such events, “Data Unavailable”</p></td></tr><tr><td>12</td><td>Error Messages</td><td>The messages in severity “ERROR” as they appear in the log</td><td>“No Error Messages” if there are no errors in the log</td></tr><tr><td>13</td><td>Fatal Messages</td><td>The messages in severity “FATAL” as they appear in the log</td><td>“No Fatal Messages” if there are no errors in the log</td></tr><tr><td>14</td><td>Ingest (*)</td><td>Total ingest</td><td><p>Calculated from cumulative writes information.</p><p>The point in time in which the value was calculated is given below the per-cf table: “(*) Data is calculated ….”</p><p>If data is not available: “No Ingest Info Available”</p></td></tr><tr><td>15</td><td>Ingest Rate</td><td>Ingest Rate in Mega-Bytes per second</td><td><p>Calculated from cumulative writes information.</p><p>The point in time in which the value was calculated is given below the per-cf table: “(*) Data is calculated ….”</p><p>If data is not available: “No Ingest Info Available”</p></td></tr><tr><td>16</td><td>Statistics</td><td>Whether statistics are available or not</td><td></td></tr><tr><td>17</td><td>Writes</td><td><p>The total number of <strong>write</strong> operations</p><p>&#x3C;Percentage> (&#x3C;Count> / &#x3C;Total Ops>)</p><p><strong>Total Ops</strong>: Total number of operations (write + read + seek)</p><p><strong>Count</strong>: Total number of writes</p><p><strong>Percentage</strong>: Percentage of writes out of Total Ops</p></td><td><p>Calculated from the <code>rocksdb.number.keys.written</code>, <code>rocksdb.number.keys.read</code>, and <code>rocksdb.number.db.seek</code> counters.</p><p>If statistics are not available: “Data Unavailable (No Statistics)”</p></td></tr><tr><td>18</td><td>Reads</td><td><p>The total number of <strong>read</strong> operations</p><p>(Same as for the Writes field, but for reads)</p></td><td>Same as for Writes</td></tr><tr><td>19</td><td>Seeks</td><td><p>The total number of <strong>seek</strong> operations</p><p>(Same as for the Writes field, but for seeks)</p></td><td>Same as for Writes</td></tr><tr><td>20</td><td>Deletes</td><td><p>&#x3C;Percentage> (&#x3C;Num Deletes> / &#x3C;Num Entries>)</p><p><strong>Num Entries</strong>: The total number of flushed entries</p><p><strong>Num Deletes</strong>: The total number of deletes in</p><p><strong>Percentage</strong>: Percentage of deletes out of Num Entries</p></td><td><p>Information is gathered from Flush events in the log.</p><p>If no such events exist: “Data Unavailable (No Flush Started Events)”</p></td></tr><tr><td>21</td><td>Num CF-s</td><td><p>Number of cf-s or</p><p>“Can't be accurately determined”</p></td><td>The log parser may not be able to know for sure the number of cf-s in the DB using the parsed log file. In that case it will show the text “Can't be accurately determined”. Please see the “Ability to determine the number of cf-s“ for more information.</td></tr><tr><td>22</td><td></td><td></td><td></td></tr><tr><td>23</td><td><strong>Column Families Information Table</strong></td><td></td><td></td></tr><tr><td>24</td><td>Column Family</td><td>The name of the CF</td><td>Will display any CF for which there was information in the log (TBD - See XXXX)</td></tr><tr><td>25</td><td>Size</td><td>The total size of all of the SST files of the CF</td><td>If no data available to calculate the size: “Data Unavailable”</td></tr><tr><td>26</td><td>Avg. Key Size</td><td>Average value of a key in newly created SST-s of the CF</td><td>Gathered from table_file_creation events in the log</td></tr><tr><td>27</td><td>Avg. Value Size</td><td>Average value of a value in newly created SST-s of the CF</td><td>Gathered from table_file_creation events in the log</td></tr><tr><td>28</td><td>Compaction Style</td><td>The compaction style used in this CF</td><td><p>Taken from the options for the CF. The values are as they appear in the log.</p><p>If not known: “UNKNOWN”</p></td></tr><tr><td>29</td><td>Compression</td><td>The compression type used in this CF</td><td><p>Taken from the options for the CF. The values are as they appear in the log.</p><p>If not known: “UNKNOWN”</p></td></tr><tr><td>30</td><td>Filter-Policy (&#x3C;BPK>)</td><td><p>The filter policy used in this CF (If any)</p><p>&#x3C;BPK> - The average BPK for the filter.</p></td><td><p>Taken from the options for the CF. The values are as they appear in the log.</p><p>If not known: “UNKNOWN”</p></td></tr></tbody></table>

## JSON File <a href="#speedbslogparsertool-jsonfile" id="speedbslogparsertool-jsonfile"></a>

Upon the user’s request, a JSON file is generated in the output folder.

The JSON file is a text file. It is best viewed by a JSON viewer. The following screen shots were taken from the Firefox browser that has a built-in JSON viewer.

JSON is a hierarchical format. The following sections describe the contents of the JSON file accordingly.

### Top Level JSON Objects <a href="#speedbslogparsertool-topleveljsonobjects" id="speedbslogparsertool-topleveljsonobjects"></a>

The JSON file contains the following top-level objects (please see the following sections for details on every object):

<figure><img src="/files/RO6xs1OSUCzxYPXA2FVO" alt=""><figcaption></figcaption></figure>

* &#x20;General: The same information that is displayed in the short console output.
* &#x20;  Options:
  * The differences between the options in the log and an applicable baseline version (if available).
  * All of the DB-Wide and per-cf options in the log
* &#x20;DB-Size: Summary information of ingest data and per-cf and level size growth.
* &#x20; Flushes: Per CF flush-related information..
* &#x20;Compactions: Per CF compactions-related information.
* &#x20;Reads: Get / Multi-Get operations related information
* &#x20;Seeks: Seek operations related information
* &#x20;Warnings: Warnings statistics.
* &#x20;Block-Cache-Stats: Statistics about the use of the block-cache.
* &#x20;CSV-s: Paths of generated CSV-s.

### JSON Top-Level Objects Detailed Description <a href="#speedbslogparsertool-jsontop-levelobjectsdetaileddescription" id="speedbslogparsertool-jsontop-levelobjectsdetaileddescription"></a>

#### General <a href="#speedbslogparsertool-general" id="speedbslogparsertool-general"></a>

This includes the same information that is displayed in the short console output. Please see that section for more details.

<figure><img src="/files/qWBGHeKjAB5pYkSTnmBF" alt=""><figcaption></figcaption></figure>

#### Options

The options object consists of 2 sub-objects:

1\.     Diff

2\.     All Options

<figure><img src="/files/dZ51hRZ9wdnoafoYfnJ2" alt=""><figcaption></figcaption></figure>

**Notes about the display of Options**

·       **WBM Pseudo-Options**: The DB-Wide options contain pseudo-options for the Write Buffer Manager. They are not true Speedb / Rocksdb options. They are configuration parameters given to the WBM during its construction. They are displayed in the log together with the “official” options as follows:

{% code lineNumbers="true" fullWidth="true" %}

```
2023/04/09-12:27:35.901291 30528                    Options.db_write_buffer_size: 0
2023/04/09-12:27:35.901299 30528                    Options.write_buffer_manager: 0x55ded4834000
                                                                             wbm.size: 107374182
                                                                            wbm.cache: 0x55deca6980f0
                                                                     wbm.allow_stalls: 1
                                                                 wbm.initiate_flushes: 1
2023/04/09-12:27:35.901304 30528         Options.access_hint_on_compaction_start: 1
```

{% endcode %}

And this is how they are displayed in the JSON (the ‘write\_buffer\_manager\_’ prefix was added):

<figure><img src="/files/Js4B0wJ9wNDZ4wosIKny" alt=""><figcaption></figcaption></figure>

·       **Block-Based-Table-Options (CF Options sub-object)**

CF Options consist of a set of top-level options. One of these options is the table\_factory. The tool assumes the table\_factory is of type `BlockBasedTableFactory` (**Block-Based-Table-Factory**). The options for this entity are displayed separately, under a sub-object called “**Block-Based Table**”.

This is an example from a log file that shows how these options are printed to the log:

{% code lineNumbers="true" fullWidth="true" %}

```
2023/04/09-12:27:35.901772 30528            Options.table_factory: BlockBasedTable
2023/04/09-12:27:35.901929 30528            table_factory options:   flush_block_policy_factory: FlushBlockBySizePolicyFactory (0x55deca6d9c60)
  cache_index_and_filter_blocks: 1
  cache_index_and_filter_blocks_with_high_priority: 1
  pin_l0_filter_and_index_blocks_in_cache: 0
  pin_top_level_index_and_filter: 1
  metadata_cache_options:
    top_level_index_pinning: 3
    partition_pinning: 0
    unpartitioned_pinning: 0
  index_type: 0
  data_block_index_type: 0
  index_shortening: 1
  data_block_hash_table_util_ratio: 0.750000
  checksum: 1
  no_block_cache: 0
  block_cache: 0x55deca6980f0
  block_cache_name: LRUCache
```

{% endcode %}

* **block\_cache\_options:** These are displayed in the log as follows:

{% code lineNumbers="true" fullWidth="true" %}

```
block_cache_name: LRUCache
block_cache_options:
  capacity : 2147483648
  num_shard_bits : 4
  strict_capacity_limit : 0
  memory_allocator : None
  high_pri_pool_ratio: 0.600
  low_pri_pool_ratio: 0.000
block_cache_compressed: (nil)
```

{% endcode %}

In the JSON, a ‘block\_cache\_’ prefix is used as follows:

<figure><img src="/files/MfHFI37HkBFZrU4X2yEG" alt=""><figcaption></figcaption></figure>

·       **metadata\_cache\_options:** These are displayed in the log as follows:

{% code lineNumbers="true" %}

```
metadata_cache_options:
  top_level_index_pinning: 3
  partition_pinning: 0
  unpartitioned_pinning: 0
index_type: 0
```

{% endcode %}

In the JSON, a ‘metadata\_cache\_’ prefix is used as follows:

<figure><img src="/files/D4w7AwhqLK8JDKGduUfp" alt=""><figcaption></figcaption></figure>

·       **Options that are pointers**

Some options are pointers (values that start with ‘0x’ and only contain hexadecimal digits). It is impossible to know from the log the real entity that the pointer points to. A pointer may or may not be initialized. Uninitialized pointers are displayed in the log in multiple ways (e.g., ;(nil)', ‘None’, etc.).

The value of an initialized pointer is meaningless in and of itself. Its value is the address of the associated entity in the address space of its containing process, and is unique within that process. Its only use, in the context of log files, is the ability to understand that the same entity is shared. For example, if the same block cache is shared between multiple cf-s, then all of them will have the same value for the block cache’s pointer option.

When the value of an initialized pointer is displayed in the JSON file, it will be displayed as “Pointer (\<pointer value>).

Uninitialized pointers will be displayed as “Pointer (Uninitialise)”.

**Options Diff**

**Overview**

<figure><img src="/files/axbMAvGpHF5LvNLQSr91" alt=""><figcaption></figcaption></figure>

This object contains the differences between the options in the log and the default options in an applicable baseline version. The baseline version is the closest available version of the applicable creator of the library (RocksDB or Speedb).

This object contains the following sub-objects:

1\.     Baseline: The version that is used as the baseline with its creator in parentheses.

2\.     Baseline Log: The full path to the baseline log file.

3\.     DB: The diff in the db options.

4\.     CF-s: Per CF options diff. This object consists of 2 sub-objects:

1\.     CF-s (Common): Contains the options that are identical in all of the cf-s in the parsed log but are different than the corresponding option in the baseline.

2\.     CF-s (Specific): Contains the options that are not identical in the parsed log, and the corresponding option in the baseline.

Please see the section “CF-s Options Diff - CF-s (Common) Sub-Object” below for more details.

**There are 3 cases with respect to a diff between the baseline and the parsed log:**

1\.     The option **exists** in the baseline but was removed in the version that generated the parsed log.

2\.     The option **doesn’t** **exist** in the baseline but was added to the version that generated the parsed log.

3\.     The option **exists in both**, but the values are different.

{% hint style="info" %}
**Notes**

·       The Options Diff object will only show options in which there is a difference.

·       Every entry in the diff contains 2 lines, the first for the baseline (called “Baseline”), and the second for the parsed log (called “Parsed Log”).

·       When an option doesn’t exist, “Missing” will be displayed.

·       If an option was **renamed** in a version, it will be deemed as a new option and it will appear twice, first for the old name (missing in parsed logs) and then for the new name (missing in the baseline).

·       Pointers handling:

o   The value of initialized pointers will always be different in the baseline and the parsed log. They will be deemed **equal** for the purposes of the comparison.

o   If both pointers are uninitialized, they will be deemed **equal**.

·       In all other cases, the values will be displayed as they appear in the log
{% endhint %}

**DB Options Diff**

This is an example of a DB options diff:

<figure><img src="/files/BURhKGI5avc9j51T2Jd7" alt=""><figcaption></figcaption></figure>

**CF-s Options Diff - CF-s (Common) Sub-Object**

The following two snapshots are an example of this sub-object:

<figure><img src="/files/Ufmx6J1vVEVR9DFNNSgi" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/fnSZFiJJM0I82ldR3z5Y" alt=""><figcaption></figcaption></figure>

**Notes**:

·       As noted above, the Common sub-object contains options whose value is identical in all of the cf-s of the parsed log. So, for example, the memtable\_factory is "speedb.HashSpdRepFactory" in all the cf-s of the parsed log. Its value is “SkipListFactory” in the baseline.

·       It consists of two sub-objects:

o   CF: The cf options that are not part of the block-based-table-format options

o   Block-Based Table: The block-based-table-format options.

**CF-s Options Diff - CF-s (Specific) Sub-Object**

<figure><img src="/files/68ICQTBIVFRxSGWClcQj" alt=""><figcaption></figcaption></figure>

**Notes:**

* &#x20;As noted above, this sub-object only contains options that are not identical in all of the cf-s of the parsed log. Please note however that all such options are included in this sub-object, even if its value is the same as the corresponding option in the baseline.
* “**Unknown-CF-#\<I>**”: As explained in the “Ability to determine the number of cf-s” section, these represent cf-s whose name can’t be determined, that have options at the top of the parsed log.

**All Options**

This object lists all of the options that appear in the log file, in the same order:

<figure><img src="/files/tsZv9rk7RUVLzeqmKLRI" alt=""><figcaption></figcaption></figure>

As in the options diff sub-object, there are sub-objects for the db-wide options and the cf-s options:

<figure><img src="/files/y0zfpUnDNLP24ZKMLdbK" alt=""><figcaption></figcaption></figure>

The sub-objects themselves, contain additional sub-objects, using the same principles described above for the options diff sub-object:

<figure><img src="/files/TdQvvSL7KHwzpxrqD5N6" alt=""><figcaption></figcaption></figure>

The **CF-s (Common)** sub-object contains all of the options that are identical in all of the cf-s of the parsed log.

The **CF-s (Specific)** sub-object contains all of the options that are not identical in all of the cf-s of the parsed log. For example:

<figure><img src="/files/Sw32RGSBIRrPuTRJpF5l" alt=""><figcaption></figcaption></figure>

As may be seen in this example, the “default” cf has options that are not the same as the corresponding options in the “column\_family\_name\_000001” and the “column\_family\_name\_000002” cf-s.

#### DB-Size Sub-Object <a href="#speedbslogparsertool-db-sizesub-object" id="speedbslogparsertool-db-sizesub-object"></a>

<figure><img src="/files/ejhNIgQfrwWz4RjkCdZK" alt=""><figcaption></figcaption></figure>

This sub-object consists of the following:

* &#x20;Ingest: Ingest information taken from the last cumulative writes log trace:
  * Ingest:
  * Ingest Rate:
  * Ingest Time:
* &#x20;CF-s Growth: Per CF and level report on the difference in the size from the start of the log to its end. The information is obtained from compaction stats dumps.
  * Per CF and level, the information is displayed as \<Start Size> **->** \<End Size> (\<Difference>)
  * The “Sum” entry shows the total for the CF (sum of all levels of the CF).

#### Flushes Object <a href="#speedbslogparsertool-flushesobject" id="speedbslogparsertool-flushesobject"></a>

<figure><img src="/files/T3ahpvSL512cQ79w7TuP" alt=""><figcaption></figcaption></figure>

This Object displays per-cf information about flushes.

Per CF, there is information **for** **all of the flushes that occurred in the CF**.

The following information is displayed, per CF:

| **Name**                           | **Meaning**                                                                                                                                                                                                                                                                                     | **Source**       |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
| L0->L1 Write-Amp                   | The write-amplification for Level0 → Level1 for this CF.                                                                                                                                                                                                                                        | Compaction Stats |
|                                    |                                                                                                                                                                                                                                                                                                 |                  |
| **Per \<Flush Reason> Sub-Object** | <p>Sub-object per flush reason in the flushes for this CF</p><p><strong>All of the fields below are in the context of a CF and flush reason</strong>.</p>                                                                                                                                       | Flush Events     |
| Sizes Histogram                    | <p>A histogram of the number of flushes per total data size range (bucket) in a flush.</p><p>The numbers are the number of flushes of all flushes for this cf and flush reason.</p><p>For example, in the snapshot above, there were 31 flushes whose total data size was more than 31 MB).</p> |                  |
| Num Flushes                        | The number of flushes                                                                                                                                                                                                                                                                           |                  |
| Min Duration                       | The minimum duration that a single flush took to complete                                                                                                                                                                                                                                       |                  |
| Max Duration                       | The maximum duration that a single flush took to complete                                                                                                                                                                                                                                       |                  |
| Min Num Memtables                  | The minimum number of memtables that were part of a single flush.                                                                                                                                                                                                                               |                  |
| Max Num Memtables                  | The maximum number of memtables that were part of a single flush.                                                                                                                                                                                                                               |                  |
| Min Total Data Size                | The minimum total data size in a single flush.                                                                                                                                                                                                                                                  |                  |
| Max Total Data Size                | The maximum total data size in a single flush.                                                                                                                                                                                                                                                  |                  |

#### Compactions Object <a href="#speedbslogparsertool-compactionsobject" id="speedbslogparsertool-compactionsobject"></a>

<figure><img src="/files/5ES1uZfaxy94ySVXUein" alt=""><figcaption></figcaption></figure>

This Object displays per-cf information about compactions.

**All of the information in this sub-objects is based on traces of compaction jobs (events and associatedlog traces), and compaction level stats dumps.**

The sub-object consists of the following:

·       Largest compaction size of all compactions in the log.

·       Per CF compactions information (see below).

Per CF Information

The per-cf compactions information is based on **all of the compactions that occurred in the CF**.

The following information is displayed, per CF:

| **Name**                           | **Meaning**                                                                                                                                               |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Per \<Flush Reason> Sub-Object** | <p>Sub-object per flush reason in the flushes for this CF</p><p><strong>All of the fields below are in the context of a CF and flush reason</strong>.</p> |
| Num Compactions                    | The number of compactions                                                                                                                                 |
| Min Compactions BW                 | Minimum write rate of a compaction                                                                                                                        |
| Max Compactions BW                 | Maximum write rate of a compaction                                                                                                                        |
| Comp                               | The elapsed time of all compactions                                                                                                                       |
| Comp Merge CPU                     | The elapsed CPU time of all compactions                                                                                                                   |
| Per Level Write-Amp                | Write amplication per level, and their total (SUM)                                                                                                        |

#### Reads Object <a href="#speedbslogparsertool-readsobject" id="speedbslogparsertool-readsobject"></a>

This object contains information about read operations performed by the user (Get and Multi-Get) and associated aspects.

<figure><img src="/files/AOWkGwsxk1G3TYzBIFHO" alt=""><figcaption></figcaption></figure>

**Get Histogram**

<figure><img src="/files/sH44btwhcuoWfEKDRzV8" alt=""><figcaption></figcaption></figure>

This is the last dump of the `rocksdb.db.get.micros` histogram.

It will be available only when statistics are enabled

**Multi-Get Histogram**

<figure><img src="/files/ZImbfprKOQDX6S0RLxTD" alt=""><figcaption></figcaption></figure>

This is the last dump of the `rocksdb.db.multiget.micros` histogram.

It will be available only when statistics are enabled

**Per CF Read Latency**

<figure><img src="/files/OmflDiFuddeKHBoXlDei" alt=""><figcaption></figcaption></figure>

Per CF, displays information about read performance across all of the CF’s levels. The information is obtained from “File Read Latency Histogram By Level” dumps:

·       Num Reads: Total number of reads.

·       Avg. Read Latency: The average read latency

·       Max Read Latency: The maximum latency

·       Read % of All CF-s: The percentage of reads performed on this CF relative to the total number of reads on all CF-s (the snapshot shows a single CF so the percentage is 100%).

**Filter Effectiveness**

<figure><img src="/files/lVtfQklWBUW8RCOjvsEF" alt=""><figcaption></figcaption></figure>

Consists of:

* CF-s: Per CF:
  * Filter-Policy: The type of filter used (if any).
  * Avg. BPK: The average effective BPK for that filter
* Counters: Global filters counters for all filters (available only if statistics are available):
  * False-Positive Rate: The effective false positive rate of all of the filters. This is displayed as a “1 in N” as it is the convention.
  * False-Positives: The number of times the filters answered “Key May Exist”, but the key wasn’t actually found.
  * Negatives: The number of times the filters answered “Key Doesn’t Exist”
  * True-Positives: The number of times the filters answered “Key May Exist” and the key was actually found.

#### Seeks Object <a href="#speedbslogparsertool-seeksobject" id="speedbslogparsertool-seeksobject"></a>

<figure><img src="/files/uvuC6PBLBSGWrdIMMaNw" alt=""><figcaption></figcaption></figure>

This Object shows information about user seek operations. The information is gathered from counters so it will be available only when statistics are enabled.

The object consists of the following:

·       Num Seeks: The number of seek operations performed by the user \[`rocksdb.number.db.seek`counter]

·       Num Found Seeks: The number of calls to seek (`SeekToLast`, `Seek`, `SeekForPrev`, and `SeekToFirst`) that succeeded (iterator was placed on a valid value) \[`rocksdb.number.db.seek.found`counter]

·       Num Nexts: The number of calls to `Next` on the iterator \[`rocksdb.number.db.next`counter]

·       Num Prevs: The number of calls to `Prev` on the iterator \[`rocksdb.number.db.prev`counter]

·       Avg. Seek Range Size: (Num Nexts + Num Prevs) / Num Seeks

·       Avg. Seeks Rate Per Second: The average number of seek operation per second during the log’s time span.

·       Avg. Seek Latency: The average seek latency taken from the `rocksdb.db.seek.micros` histogram.

#### Warnings Object <a href="#speedbslogparsertool-warningsobject" id="speedbslogparsertool-warningsobject"></a>

<figure><img src="/files/Pqju06SESYq5VFNrzFT6" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/QpnGyO7PK6Ut2e9ByTWa" alt=""><figcaption></figcaption></figure>

This object contains minimal summary information about the warnings found in the log.

The object consists of the following:

* DB: Warnings that are not associated with a specific cf. This will be displayed the number of such warnings. Currently all of the DB warnings are not categorized (“Other”).
* CF-s: Warning that are associated with a CF. Per CF:
  * A histogram of the number of warnings per category of warnings. Existing categories include:
    * Write-Stop: The database warns about a write stop
    * Write-Delay: The data base warns about a write delay
    * Other: Any warning not of one of the categories listed above.

#### Block-Cache-Stats Objects <a href="#speedbslogparsertool-block-cache-statsobjects" id="speedbslogparsertool-block-cache-statsobjects"></a>

This object contains block cache information and associated statistics found in the log (if available).

If you are unfamiliar with RocksDB’s block cache, please read [RocksDB Block Cache](https://github.com/facebook/rocksdb/wiki/Block-Cache).

Please note the following:

·       The block cache is optional and is configured per cf.

·       **Block Cache Sharing**:

o   Multiple cf-s may share the same block cache.

o   Multiple db-s may share the same block cache. However, there is no information in a single log file that provides any information about the sharing. RocksDB displays the information for the entire cache, so it’s impossible to know how it is used by the entities that share it (cf-s and db-s). Speedb adds per-cf information about the usage of the cache.

·       **Cache-Id**: A block cache is uniquely identified by its id (e.g., ‘LRUCache\@0x7fde57dbf300#1’ in the image below). The cache’s id consists of 3 parts - \<name>@\<pointer>#\<process-id>

o   Name: The value of the block\_cache\_name field in the cf’s table options ('LRUCache').

o   Pointer: The value of the block\_cache field in the the cf’s table options ('0x7fde57dbf300'). This value may serve as a unique identifier for a specific cache entity within a process.

o   Process-id: The process id of the process that generated this log ('1'). The log parser may or may not display this part of the name.

·       **Cache Roles**: The cache classifies its contained blocks according to their type or role. Statistics are gathered for specific roles. All other roles are grouped under the ‘Misc’ role. The role’s name indicates its type (e.g., ‘FilterBlock’ is for Filter blocks). The ‘WriteBuffer’ role is shown when the Write Buffer Manager is configured to cost its reservations in the block cache.

·       The block cache is configured with a capacity. However, unless it is configured to use strict capacity (which is not done in practice), the usage may exceed the capacity.

**Top-level sub-objects**

<figure><img src="/files/OPrfpTyBie7tl9nBwpJM" alt=""><figcaption></figcaption></figure>

There are 3 top-level sub-objects

1\.     Caches

2\.     DB Counters

3\.     Detailed

They are described in detail below.å

**Caches top-level sub-object**

<figure><img src="/files/mUVHKGJOmFnuNU1NBgPI" alt=""><figcaption></figcaption></figure>

This sub-object consists of the following:

* &#x20;Per cache-id (cache entity - “LRUCache\@0x55deca6980f0”):
  * **Capacity**: The configured cache’s capacity.
  * **Num Shards**: The number of shards to which it is divided (2 ^ num\_shard\_bits).
  * **Shard Size**: The size of a single shared (capacity / number of shards)
  * **CF-s:**
    * Per-cf value of the `cache_index_and_filter_blocks` cf block based table option.
  * &#x20;**Index Block / Filter Block**: Statistics for the Index and Filter blocks that belong to cf-s that are part of this DB and share this cache. The data is obtained from `table_file_creation` events, so it is independent of its containment in the cache. The following data is displayed:
    * Avg. Size: The average size of a block of that type.
    * Max Size: The largest block of that type
    * Max Size At: At what time was the table that contained the largest block created.

**DB Counters top-level sub-object**

<figure><img src="/files/T65TLU9Mi2coDLNMcJh2" alt=""><figcaption></figcaption></figure>

{% code lineNumbers="true" fullWidth="true" %}

```
2023/04/09-12:27:46.176881 30594 [/db_impl/db_impl.cc:807] STATISTICS:
 rocksdb.block.cache.miss COUNT : 61305
rocksdb.block.cache.hit COUNT : 25902952
rocksdb.block.cache.add COUNT : 60821
rocksdb.block.cache.add.failures COUNT : 0
rocksdb.block.cache.index.miss COUNT : 24
rocksdb.block.cache.index.hit COUNT : 6152077
rocksdb.block.cache.index.add COUNT : 24
rocksdb.block.cache.index.bytes.insert COUNT : 1538944
rocksdb.block.cache.index.bytes.evict COUNT : 0
rocksdb.block.cache.filter.miss COUNT : 24
rocksdb.block.cache.filter.hit COUNT : 13625205
rocksdb.block.cache.filter.add COUNT : 24
rocksdb.block.cache.filter.bytes.insert COUNT : 4558592
rocksdb.block.cache.filter.bytes.evict COUNT : 0
rocksdb.block.cache.data.miss COUNT : 61257
rocksdb.block.cache.data.hit COUNT : 6125677
rocksdb.block.cache.data.add COUNT : 60773
rocksdb.block.cache.data.bytes.insert COUNT : 253748240
```

{% endcode %}

This sub-object includes block cache miss and hit counters:

·       **cache\_add / cache\_miss cache\_hit**: Total number of cache misses / hits / adds (for all roles)

·       **index\_add / index\_miss / index\_hit**: Number of index block cache misses / hits / add&#x73;**.**

·       **filter\_add / filter\_miss / filter\_hit:** Number of filter block cache misses / hits / add&#x73;**.**

·       **data\_add / data\_miss / data\_hit:** Number of filter block cache misses / hits / add&#x73;**.**

**Detailed top-level sub-object**

<figure><img src="/files/NEx1u9LyG7eLJfhy9pON" alt=""><figcaption></figcaption></figure>

This sub-object includes the individual processed block cache stats dumps such as the following:

{% code lineNumbers="true" fullWidth="true" %}

```
Block cache LRUCache@0x55deca6980f0#30528 capacity: 2.00 GB usage: 878.27 KB table_size: 256 occupancy: 16 collections: 1 last_copies: 2 last_secs: 6.2e-05 secs_since: 0
Block cache entry stats(count,size,portion): FilterBlock(7,459.22 KB,0.0218973%) IndexBlock(7,161.55 KB,0.00770316%) WriteBuffer(1,256.00 KB,0.012207%) Misc(1,0.00 KB,0%)
```

{% endcode %}

Per cache-id, the following **global** information:

·       **Capacity**: The block cache capacity (as configured)

·       **Usage**: The usage at the end of the log

This global information is followed by the individual dumps that are printed throughout the log. Per dump time, the following information is displayed, that reflects the state at that time:

·       **Usage**: The usage

·       **Per role** (Index / Filter / Write-Buffer / Misc):

o   **Count**: The number of blocks of that role currently in the cache

o   **Size**: The total size of those blocks

o   **Portion**: Their percentage of the total cache’s capacity

### CSV Files <a href="#speedbslogparsertool-csvfiles" id="speedbslogparsertool-csvfiles"></a>

During a run, the tool generates multiple CSV files. These files are best viewed with a spreadsheet viewing and editing software (e.g., LibreOffice Calc, Google Sheets, Microsoft Excel, etc.). These files are also an excellent input for further analysis and processing by applicable tools.

It may be that there is no applicable data in the parsed log to allow the generation of a CSV file (e.g., statistics are unavailable) . The tool reports, per CSV file, whether it was generated or not, and, if it was, the full path of the file.

The following table lists summarizes this:

| **CSV File name**                                                | **Source**                                                         | **Generation Info**        |
| ---------------------------------------------------------------- | ------------------------------------------------------------------ | -------------------------- |
| counters.csv                                                     | Statistics dump                                                    | statistics must be enabled |
| <p>histograms\_human\_readable.csv /<br>histogram\_tools.csv</p> | Statistics dump                                                    | statistics must be enabled |
| compaction\_stats.csv                                            | Compaction stats dumps                                             |                            |
| compactions.csv                                                  | Compaction events (`compaction_started` and `compaction_finished`) |                            |
| flushes.csv                                                      | Flush events (`flush_started` and `flush_finished`)                |                            |

The sections that follow provide detailed information about the individual csv files.

#### counters.csv <a href="#speedbslogparsertool-counters.csv" id="speedbslogparsertool-counters.csv"></a>

This file contains the values of the dump of the counters in the log:

{% code lineNumbers="true" fullWidth="true" %}

```
2023/04/09-12:37:27.399291 30594 [/db_impl/db_impl.cc:807] STATISTICS:
 rocksdb.block.cache.miss COUNT : 2174509
rocksdb.block.cache.hit COUNT : 1626824082
rocksdb.block.cache.add COUNT : 2138479
rocksdb.block.cache.add.failures COUNT : 0
rocksdb.block.cache.index.miss COUNT : 881
```

{% endcode %}

<figure><img src="/files/GRgT848KRkxHFqKv6cJh" alt=""><figcaption></figcaption></figure>

This file is generated only when statistics are enabled.

The header line contains the names of the counters (e.g., ‘rocksdb.block.cache.miss’), in the order they appear in the log.

The row consists of the timestamp of the dump, followed by the values of all the counters at that time.

The dump to the log includes all of the tracked counters, even if their value is always 0. The CSV will **not** show counters whose values are always 0.

#### Counters Histograms CSV-s <a href="#speedbslogparsertool-countershistogramscsv-s" id="speedbslogparsertool-countershistogramscsv-s"></a>

These csv-s are for the dump of the counters histograms in the log. These are available when statistics are enabled

{% code lineNumbers="true" fullWidth="true" %}

```
rocksdb.db.get.micros P50 : 8.575028 P95 : 29.481907 P99 : 347.891977 P100 : 108208.000000 COUNT : 432304103 SUM : 8619564063
rocksdb.db.write.micros P50 : 299.321021 P95 : 1147.649366 P99 : 1865.648518 P100 : 159096.000000 COUNT : 48033546 SUM : 19528994334
rocksdb.compaction.times.micros P50 : 458512.396694 P95 : 702312.500000 P99 : 737699.000000 P100 : 737699.000000 COUNT : 174 SUM : 78440025
rocksdb.compaction.times.cpu_micros P50 : 418348.623853 P95 : 545480.000000 P99 : 545480.000000 P100 : 545480.000000 COUNT : 174 SUM : 67217901
rocksdb.subcompaction.setup.times.micros P50 : 0.000000 P95 : 0.000000 P99 : 0.000000 P100 : 0.000000 COUNT : 0 SUM : 0
rocksdb.table.sync.micros P50 : 2280.519481 P95 : 4275.000000 P99 : 9133.235294 P100 : 25031.000000 COUNT : 695 SUM : 1742121
rocksdb.compaction.outfile.sync.micros P50 : 2267.521368 P95 : 5060.000000 P99 : 9551.142857 P100 : 12668.000000 COUNT : 174 SUM : 416764
```

{% endcode %}

Two files are generated, one is aimed at a human reader (histograms\_human\_readable.csv) and the other at tools that will process the CSV (histograms\_tools.csv). Both files contain the same information, just arranged differently.

**histograms\_human\_readable.csv**

<figure><img src="/files/7qn1xJOKNY6jHiAZazTb" alt=""><figcaption></figcaption></figure>

**histograms\_tools.csv**

<figure><img src="/files/Rr9RmtnP4LgxcY9yPAFE" alt=""><figcaption></figcaption></figure>

#### compactions\_stats.csv

This CSV includes the fields of the Compaction stats dumps:

{% code lineNumbers="true" fullWidth="true" %}

```
** Compaction Stats [column_family_name_000019] **
Level    Files   Size     Score Read(GB)  Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  L0      4/0   982.88 MB   3.8      0.0     0.0      0.0       4.8      4.8       0.0   1.0      0.0    399.5     12.30              7.12        20    0.615       0      0       0.0       0.0
  L1      1/0   433.47 MB   1.7      4.6     3.8      0.7       4.5      3.8       0.0   1.2    543.1    540.4      8.59              7.37         5    1.718   4736K    23K       0.0       0.0
  L2      4/0    3.34 GB   1.3      6.4     2.9      3.4       6.3      2.9       0.5   2.2    261.6    259.6     24.89             14.18         3    8.297   6611K    51K       0.0       0.0
 Sum      9/0    4.73 GB   0.0     10.9     6.8      4.2      15.6     11.5       0.5   3.3    244.2    349.9     45.78             28.67        28    1.635     11M    75K       0.0       0.0
 Int      0/0    0.00 KB   0.0     10.9     6.8      4.2      15.6     11.5       0.5   3.3    244.2    349.9     45.78             28.67        28    1.635     11M    75K       0.0       0.0
```

{% endcode %}

<figure><img src="/files/95RXU5lGneLrhcB6gtMe" alt=""><figcaption></figcaption></figure>

#### compactions.csv <a href="#speedbslogparsertool-compactions.csv" id="speedbslogparsertool-compactions.csv"></a>

This CSV includes the fields of the compaction\_started and compaction\_finished events:

{% code lineNumbers="true" fullWidth="true" %}

```
2023/04/09-12:27:38.140657 30538 EVENT_LOG_v1 {"time_micros": 1681032458140646, "job": 4, "event": "compaction_started", "compaction_reason": "LevelL0FilesNum", "files_L0": [159, 150, 149, 146], "files_L1": [141], "score": 1, "input_data_size": 44710877, "oldest_snapshot_seqno": -1}
```

{% endcode %}

{% code lineNumbers="true" fullWidth="true" %}

```
2023/04/09-12:27:38.442191 30538 (Original Log Time 2023/04/09-12:27:38.442131) EVENT_LOG_v1 {"time_micros": 1681032458442117, "job": 4, "event": "compaction_finished", "compaction_time_micros": 300003, "compaction_time_cpu_micros": 297596, "output_level": 1, "num_output_files": 1, "total_output_size": 28850078, "num_input_records": 516430, "num_output_records": 333334, "num_subcompactions": 1, "output_compression": "NoCompression", "num_single_delete_mismatches": 0, "num_single_delete_fallthrough": 0, "lsm_state": [0, 1, 0, 0, 0, 0, 0]}
```

{% endcode %}

There is a large number of fields so only a subset is shown here. Please view an actual generated compactions.csv to see all of the fields.

<figure><img src="/files/jN7nfnj94a6LmLPc9LdZ" alt=""><figcaption></figcaption></figure>

#### flushes.csv <a href="#speedbslogparsertool-flushes.csv" id="speedbslogparsertool-flushes.csv"></a>

{% code lineNumbers="true" fullWidth="true" %}

```
2023/04/09-12:27:39.658098 30540 EVENT_LOG_v1 {"time_micros": 1681032459658093, "job": 6, "event": "flush_started", "num_memtables": 1, "num_entries": 85137, "num_deletes": 0, "total_data_size": 7662330, "memory_usage": 9538016, "flush_reason": "Write Buffer Manager Initiated"}
```

{% endcode %}

{% code lineNumbers="true" fullWidth="true" %}

```
2023/04/09-12:27:39.743132 30540 (Original Log Time 2023/04/09-12:27:39.743070) EVENT_LOG_v1 {"time_micros": 1681032459743064, "job": 6, "event": "flush_finished", "output_compression": "NoCompression", "lsm_state": [3, 1, 0, 0, 0, 0, 0], "immutable_memtables": 0}
```

{% endcode %}

Contains a subset of the fields from the flush\_started and flush\_finished events

<figure><img src="/files/UHw1TFvFVbWXcfM6tRXO" alt=""><figcaption></figcaption></figure>

## Testing

There are unit tests for the tool under the test folder. The tests are written in the pytest framework.

[pytest](https://docs.pytest.org/en/7.3.x/) installation

{% code lineNumbers="true" %}

```
pip install pytest
```

{% endcode %}

Running the unit tests:

{% code lineNumbers="true" %}

```
cd test
pytest
```

{% endcode %}

The tool should pass all of the unit tests.

A successful run should like like this:

{% code lineNumbers="true" fullWidth="true" %}

```
udi@udi-speedb:~/log-parser/test$ pytest
========================================================= test session starts ==========================================================
platform linux -- Python 3.8.10, pytest-7.2.1, pluggy-1.0.0
rootdir: /home/udi/log-parser/test
plugins: anyio-3.6.2
collected 139 items                                                                                                                    

test_baseline_log_files_utils.py ....                                                                                            [  2%]
test_cache_utils.py .                                                                                                            [  3%]
test_calc_utils.py .......                                                                                                       [  8%]
test_cfs_infos.py .........                                                                                                      [ 15%]
test_compactions.py ...                                                                                                          [ 17%]
test_counters.py .....                                                                                                           [ 20%]
test_csv_outputter.py ...                                                                                                        [ 23%]
test_db_files.py ..                                                                                                              [ 24%]
test_db_options.py ...............                                                                                               [ 35%]
test_events.py ...................                                                                                               [ 48%]
test_log_entry.py .....                                                                                                          [ 52%]
test_log_file.py ..............                                                                                                  [ 62%]
test_log_file_options_parser.py .......                                                                                          [ 67%]
test_regexes.py .....                                                                                                            [ 71%]
test_stats_mngr.py ....................                                                                                          [ 85%]
test_utils.py .................                                                                                                  [ 97%]
test_warnings_mngr.py ...                                                                                                        [100%]

========================================================= 139 passed in 0.26s ==========================================================
```

{% endcode %}

## Miscellaneous <a href="#speedbslogparsertool-miscellaneous" id="speedbslogparsertool-miscellaneous"></a>

### Reporting Bugs and Proposing <a href="#speedbslogparsertool-reportingbugsandproposing" id="speedbslogparsertool-reportingbugsandproposing"></a>

If you find a bug or wish to propose a new feature or an improvement in an existing feature, please open an issue in our [GitHub](https://github.com/speedb-io/log-parser/issues)

### Known Bugs <a href="#speedbslogparsertool-knownbugs" id="speedbslogparsertool-knownbugs"></a>

The major issues that and known bugs is listed in the project’s [GitHub](https://github.com/speedb-io/log-parser/issues).

### Contributing to the tool’s development <a href="#speedbslogparsertool-contributingtothetoolsdevelopment" id="speedbslogparsertool-contributingtothetoolsdevelopment"></a>

The log parser is an Speedb open source project. Contributions to its development are very welcome. Please see the [README.md](http://README.md) in the repository for details.

### Getting Advice or Discussing the Tool <a href="#speedbslogparsertool-gettingadviceordiscussingthetool" id="speedbslogparsertool-gettingadviceordiscussingthetool"></a>

Please use the following [**Discord Channel**](https://discord.gg/gry235dZRh) to get assistance, ask questions, etc.

### The Future (the Features) <a href="#speedbslogparsertool-thefuture-thefeatures" id="speedbslogparsertool-thefuture-thefeatures"></a>

Requests for new features will be opened in [GitHub](https://github.com/speedb-io/log-parser/issues)


# DB\_bench: Groups

## Overview

This is a usability enhancement for db\_bench.

Currently,  db\_bench allows running multiple benchmarks in a single invocation, for example:

./db\_bench -benchmarks fillseq,flush,overwrite …. \[other flags as applicable]

However, all of the benchmarks share the same configuration.

A user could run db\_bench multiple times on an existing Database(s), each run with its own configuration, by specifying the ‘-use\_existing\_db’ flag. For example, say the user wanted to do a long fillup of a database, followed by multiple benchmarks, on the filled up database. That user could do that by running the following commands:

./db\_bench -benchmarks fillrandom -duration 3600 -threads 50 …

./db\_bench -benchmarks overwrite -use\_existing\_db -duration 60 -report\_file overwrite.csv …

./db\_bench -benchmarks readrandom  -use\_existing\_db -duration 60 -report\_file readrandom.csv …

However, whenever db\_bench exits (at the end of each run), it closes the database (and flushes the memtables).

This new feature allows a user to run db\_bench once, but specify groups of benchmarks, each with its own configuration, as if they were run separately. For example, the above 3 commands could be combined using the new feature as follows:

./db\_bench -groups “-benchmarks fillrandom -duration 3600 -threads 50 …“ “-benchmarks overwrite -duration 60 -report\_file overwrite.csv …“ “-benchmarks readrandom  -duration 60 -report\_file readrandom.csv …“

The same 3 benchmarks would run (each has its own group), each with its own desired configuration. The difference is that the DB(s) remain open between groups. So, when one group ends and a new one starts, the database retains its state. Specifically, the database stays open, and the memtables are not flushed.

## Syntax

&#x20;./db\_bench -groups '\<group1>' '\<group2>' '\<group3>' …

## Notes

* Each group has its own configuration. A group’s configuration is a set of valid db\_bench flags, exactly as if they were given in the normal db\_bench invocation (single-group mode).
* &#x20;The first group (the MASTER group) sets the initial configuration for all subsequent groups. Subsequent groups may override the initial configuration (some limitations apply, see below). Please note that the master group’s configuration is the default for any of the following groups. So, each group following the first starts with the master’s configuration, and may override or add to that.
* As noted above, the database(s) are opened when running the first group and  remain open thereafter. They are closed, as always, when db\_bench exits. However, please note that:
* * Any flag that affects the options may only be set during the first group. These flags will be silently ignored in subsequent groups (e.g., flags such as ‘-write\_buffer\_size’, or ‘max\_write\_buffer\_number’).
  * The following flags may only be set for the first group. Attempting to override them in subsequent groups will cause db\_bench to abort when running the violating group:
  * * -db
    * -statistics
    * -statistics\_string
    * -env\_uri
    * -fs\_uri
    * -simulate\_hdd
    * -simulate\_hybrid\_fs\_file
    * -simulate\_hybrid\_fs\_file;
    * -simulate\_hybrid\_hdd\_multipliers
    * -simulate\_hybrid\_hdd\_multipliers;
    * &#x20;-seed
* The ‘-groups’ or ‘--groups’ keyword indicates that the user works in groups mode.
* The ‘groups’ keyword is not a gflags flag. It is displayed in the db\_bench’s help as do all other flags, but, again, is not a gflags flag:
* * It must be the first “flag”, right after the db\_bench executable’s name
  * It is not passed to gflags for parsing as do all other flags.
  * If the “-groups” keyword is missing or misplaced, db\_bench assumes the default, “single-group” mode, that was the only one thus far.
* Every group is delimited by ‘’ (single-quotes) or “” (double-quotes). When values of flags themselves need to use quotes, the user should just use one type of quotes as  the group’s delimiter, and the other for the values.

### Additional Examples

&#x20;./db\_bench -groups '-num 100 -benchmarks "fillseq,readrandom"' '-num 200  -benchmarks readrandom' '-benchmarks readrandom -reads 10000'

Group 1

* The fillseq,readrandom benchmarks will run.
* -num = 100
* All other flags have their default values as usual.

Group 2

* The readrandom benchmark will run.
* -num = 200 (set by the group 1 and not overridden).
* All other flags retain their (default) values from group 1

Group 3

* The readrandom benchmark will run
* -num = 1000 (overriding -num of group 1).
* -reads = 1000 (overriding the default (implicit) value of -reads set internally by db\_bench)

&#x20;

<br>


# Beezcli Tool

Speedb Interactive tool

## Introduction

The beezcli command line tool offers multiple data access and database admin commands. beezcli supports interactive mode with history and it will remember the --db and --secondary\_path once the interactive mode is being enabled.

beezcli is wrapping RocksDB's and LevelDB's ldb tool.

Some examples are listed below. For more information, please consult the help message displayed when running beezcli without any arguments or with --help.

\
How to use it?
--------------

The beezcli tool is available under the [tools](https://github.com/speedb-io/speedb/tree/main/tools) directory in Speedb repository.&#x20;

By default, \`beezcli\` can only be used against a DB that is offline. Operating the DB, even for read-only operations, might make changes to the DB directory, e.g. info logs.

#### Open as secondary

An option \`--secondary\_path=\<secondary\_path>\` would open the DB as a \[\[Secondary instance]], which can be used to open a running DB and/or to minimize impacts to the DB directory. This argument can be used for any beezcli command, but since not all operations can be done against secondary instances, some operations will fail. Besides write operations which would definitely fail with secondary instances, some read operations might also fail.

## Compilation and dependencies

### &#x20;Dependencies

\- cmake

\- make

\- gflags

\- snappy

\- zlib

\- bzip2

\- zstandard

\- lz4

\- gnu readline

### &#x20;How to compile

For mac os and Linux:

````
```zsh
git clone https://github.com/ofriedma/speedb beezcli
cd beezcli
mkdir build
cmake .. -DCMAKE_BUILD_TYPE=Release -DWITH_CORE_TOOLS=ON -DWITH_TOOLS=ON
make -j $(nproc) beezcli 
````

{% hint style="info" %}
Notice: Currently the tool is not supported on Windows
{% endhint %}

#### Example for interactive mode

Example data access:

```bash
$./beezcli --interactive --db=/tmp/test_db
/tmp/test_db
beezcli> put a1 b1 --create_if_missing
OK
beezcli> get a1
b1
beezcli> delete a1
OK
beezcli> get a1
Failed: Get failed: NotFound:
beezcli> quit


Ciao

```

Example dump database:

```bash
$./beezcli --interactive --db=/tmp/test_db
/tmp/test_db
beezcli> put a1 b1 --create_if_missing
OK
beezcli> dump
a1 ==> b1
Keys in range: 1
beezcli> dump --no_value
a1
Keys in range: 1
beezcli> dump --count_only

beezcli> idump
'a1' seq:0, type:1 => b1
Internal keys in range: 1
beezcli> delete a1
OK
beezcli> idump
'a1' seq:4, type:0 => 
'a1' seq:0, type:1 => b1
Internal keys in range: 2
```

Example data access sequence:

```bash
$./beezcli --db=/tmp/test_db --create_if_missing put a1 b1
    OK 
    $ ./beezcli --db=/tmp/test_db get a1
    b1
 
    $ ./beezcli --db=/tmp/test_db get a2
    Failed: NotFound:
    
     $ ./beezcli --db=/tmp/test_db scan
    a1 : b1
 
    $ ./beezcli --db=/tmp/test_db scan --hex
    0x6131 : 0x6231
 
    $ ./beezcli --db=/tmp/test_db put --key_hex 0x6132 b2
    OK
 
    $ ./beezcli --db=/tmp/test_db scan
    a1 : b1
    a2 : b2
  
    $ ./beezcli --db=/tmp/test_db get --value_hex a2
    0x6232
 
    $ ./beezcli --db=/tmp/test_db get --hex 0x6131
    0x6231
 
    $ ./beezcli --db=/tmp/test_db batchput a3 b3 a4 b4
    OK
 
    $ ./beezcli --db=/tmp/test_db scan
    a1 : b1
    a2 : b2
    a3 : b3
    a4 : b4
 
    $ ./beezcli --db=/tmp/test_db batchput "multiple words key" "multiple words value"
    OK
 
    $ ./beezcli --db=/tmp/test_db scan
    Created bg thread 0x7f4a1dbff700
    a1 : b1
    a2 : b2
    a3 : b3
    a4 : b4
    multiple words key : multiple words value
```

To dump an existing speedb database in HEX:

```bash
$ ./beezcli --db=/tmp/test_db dump --hex > /tmp/dbdump
```

To load the dumped HEX format data to a new Speedb database:

```bash
$ cat /tmp/dbdump | ./beezcli --db=/tmp/test_db_new load --hex --compression_type=bzip2 --block_size=65536 --create_if_missing --disable_wal
```

<br>

To compact an existing Speedb database:

```bash
$ ./beezcli --db=/tmp/test_db_new compact --compression_type=bzip2 --block_size=65536
```

You can specify the command line \`--column\_family=\<string>\` for which column family your query will be against.

\`--try\_load\_options\` will try to load the options file in the DB to open the DB. It is a good idea to always try to have this option on when you operate the DB. If you open the DB with default options, it may mess up LSM-tree structure which can't be recovered automatically.

<br>

<br>


# RocksDB Basics

A high-level overview of RocksDB objects and operations

[RocksDB Overview](#rocksdb-overview)\
[LSM](#log-structure-merge-lsm)\
[Memtable](#memtable)\
[Write (Put)](#write-put)\
[Read (Get)](#read-flow-get)\
[Key tombstones (Delete)](#key-tombstones-delete)\
[LSM levels and compaction](#lsm-levels-and-compaction)\
[LSM compaction tradeoffs\
Leveld compaction](#lsm-compaction-tradeoffs)\
[General block-based SST structure](#general-block-based-sst-structure)\
[Column Families<br>](#column-families)[WAL<br>](#the-wal)[Iterators<br>](#iterators)[Snapshots](#snapshots)\
[Merge Operators<br>](#merge-operators)[Range Deletion<br>](#range-delete)[Single delete](#single-delete)

## RocksDB Overview

RocksDB is a key value storage engine based on Log-Structures Merge (LSM) Tree. It is developed by Meta and a fork of LevelDB (Google).

### Log Structure Merge (LSM)

LSM tree is an append only data structure, optimized for write-heavy workloads.

The LSM Tree is a variant of the traditional B-tree data structure and is commonly used in distributed storage systems and databases like Apache Cassandra, LevelDB, and RocksDB.

The LSM Tree organizes data into multiple levels, each level consisting of multiple sorted structures, typically called memtables and SSTables (Sorted String Tables). Here's a high-level overview of the LSM Tree process:

1. Write Path: When data is inserted or updated in an LSM Tree-based system, it is first written to an in-memory data structure called the memtable. The memtable allows for fast and efficient writes since it resides in memory. Once the memtable reaches a certain threshold, it is flushed to disk as an SSTable.
2. Compaction: Over time, multiple SSTables accumulate on disk as data is written and flushed from memtables. These SSTables can contain redundant or overlapping data. The compaction process merges and consolidates these SSTables to optimize storage space and improve read performance. During compaction, the SSTables are merged, sorted, and deduplicated, resulting in a compacted SSTable with only the latest and non-redundant data.
3. Read Path: When a read operation is performed, the LSM Tree checks the memtable first, as it contains the most recent writes. If the data is not found in the memtable, it looks for the data in the SSTables, starting from the most recent and going down the levels until the data is found or determined to be absent.

The LSM Tree structure and compaction process provide several advantages, including efficient write operations, high throughput, and scalability. However, it also introduces some trade-offs, such as increased read amplification due to the need to search through multiple SSTables during read operations. These trade-offs are carefully managed and optimized in LSM Tree-based systems to provide high-performance and reliable data storage and retrieval.

LSM consists of the following main two components:

1. Memtable, which is a memory-resident write buffer
2. SST (Sorted Static/String Table), which is a disk resident and includes immutable collection of key-value pairs

#### Memtable

A memory resident append only data structure that holds key-value pairs. It acts as a write buffer for incoming writes, and serves reads for data that it holds (more on that later)

Due to the need to support efficient ordered iteration, the default RocksDB memtable is an ordered singly-linked skip list, providing an average O(log N) complexity for both insertion and retrieval operations.

#### Write (Put)

* ncoming writes are applied to the memtable
* When certain conditions are met (more on that later) a memtable switch is performed, where the active memtable becomes immutable and flushed to disk, and a new memtable is created to accept new writes
* The flushed memtable is written as an ordered sequence of key-value pairs into an SST file

SST files are immutable after creation, and data in them cannot be modified or carved out of them (more on that later)

\\

<figure><img src="/files/ofzO2QaKjumh3k9srIh8" alt="" width="198"><figcaption></figcaption></figure>

#### Read flow (Get)

* Check the active memtable
* Check immutable memtables, if any
* Check SST files, from newest to oldest

This isn’t great for read performance, especially as SST files accumulate

<figure><img src="/files/Z6ZAcnrhA35Ha9H8XiD3" alt="" width="374"><figcaption></figcaption></figure>

#### Key tombstones (Delete)

Because each SST file is immutable and we cannot just punch holes in existing SST files, to support key deletion, LSM stores employ a technique called tomb-stoning

A tombstone is a special record that only contains a key (without value) indicating that the key is deleted

A tombstone is said to “cover” an existing key record because a read always checks newer data first, so when a tombstone is encountered the read can complete immediately, knowing that the key was deleted

\
![](/files/PhiiTETjfjXNq4J6pfFW)

### LSM Levels and Compaction

LSM stores data in multiple levels to enhance read performance. Since each level contains sorted runs, read operations can be efficiently performed by sequentially scanning the levels from the highest to lowest, gradually merging and retrieving the required data. This approach reduces random disk access and enhances overall read efficiency. (L0 to LN).

The SST files within each level aren't overlapping (except in L 0), so a GET only needs to do roughly as many reads as the number of levels.

Compaction is the process of moving data between levels in the background.

The process of compaction is called that way because it chooses two or more overlapping SST files from one or more levels and combines them into a new sorted collection of key-value pairs, discarding older versions of keys and tombstoned (possibly creating more than a single output SST).

<figure><img src="/files/anPaZMu6JDLELbXSXVZs" alt="" width="183"><figcaption></figcaption></figure>

#### LSM compaction tradeoffs

* Space Amplification -- keeping multiple versions of the same key increases the amount of space taken by SST files
* Write Amplification -- compacting too frequently a range of keys increases the amount of data being written to disk, harming disk endurance and eating into the disk I/O bandwidth
* Read Amplification -- more overlapping sorted runs means more checks on every GET, incurring more slow disk read I/O

#### Leveled compaction

A compaction strategy chooses an entire level once certain conditions are met (mainly, when the level reaches its size limit), and compacts it into a lower level by choosing the overlapping files from the lower level and combining them.

Since on L0 the SST files are just dumps of the data from the memtable, it doesn’t constitute a single sorted run and files can (and usually do) overlap, which effectively adds more “levels” that a GET request needs to check. As a result, workloads that care about read performance should keep the number of files in L 0 to a minimum.

<figure><img src="/files/jX0WhUc6FPvOG8IVRSUV" alt="" width="231"><figcaption></figcaption></figure>

\\

#### More compaction Methods

* Universal Compaction (aka Tiered Compaction) -- trades lower read and space amplifications for lower write amplification as it tries to merge only sorted runs that are of similar size and cover roughly the same key ranges (as opposed to leveled compaction which always compacts a smaller sorted run into a larger one)
* FIFO Compaction (aka TTL compaction) -- discards whole SST files as their age exceeds a set threshold (useful for time-series data, for example)

### General block-based SST structure

The data on the SST is divided into logical blocks.

A block is just a single read-and-write unit, not defined by a hard size limit.

There are many block types, but these are the most relevant:

* Data
* Index
* Filter

![](/files/JnEUbrVyw7WHP2gMllnx)

#### The filter block (optional)

The filter block is used to avoid disk I/O during read by skipping sorted runs which do not contain the lookup key.

The filter block utilizes a probabilistic data structure which may return a false positive (key exists when it doesn’t), but never a false negative.

RocksDB includes filter policy implementations based on Bloom Filter and on Ribbon Filter.

The filter block contains the serialized data used by the filter policy to determine if a lookup key exists or not.

RocksDB does not (currently) create a filter policy by default, because it consumes additional memory and storage, so SST files created with the default settings will not contain a filter block.

#### Data Block

Variable length blocks, broken after the first key-value pair which crosses the configured block\_size boundary.

The data block contains the raw data of the key-value pairs encoded as variable-length strings.

Keys are also delta-encoded by default to save space (which means that we can’t just seek into the beginning of a key and read it, because it’s only the delta from the previous key).

Data blocks contain some metadata in addition to the key-value pairs.

#### The index block

Because data blocks have variable lengths and in order to avoid doing expensive I/O when we search for the block containing the lookup key, an index block is created for each SST file.

Essentially a list containing the first key of each block and the offset at which that block begins.

By default the keys in the index block are delta-encoded as well.

### Column Families

column family is a logical grouping of key-value pairs within the database. It allows for data organization and management, providing a way to group related data together while maintaining separate options and configurations for each group. \\

With Column families you can group logically similar data together (akin to tables in a relational database), while still providing a holistic view of the data in the entire database.

Each column family is essentially a separate LSM-tree, with its own memtable, compaction and data layout configuration.

Column families share resources in the database, so they can be aware of what is happening in other column families when maintenance operations are required.

### Write Ahead Log (WAL)

In order to prevent data loss upon premature shutdown (due to new data being written to the memory-resident memtable), every write batch is also written to disk in a Write Ahead Log file (aka the journal in some databases).

In order to uphold the atomicity guarantee of the write batch across column families, the WAL is shared between all column families in a database.

On each startup the WAL is replayed to arrive at the state of the database before shutdown.

RocksDB provides many configuration options for the WAL, including (but not limited to):

* Maximum size of a single WAL file
* Total size of the WAL per database
* Amount of data written to the WAL which will trigger a disk sync
* Avoiding writing to the WAL for a specific write batch
* How to deal with corruption in the WAL during replay

### The MANIFEST

The version information is written into the MANIFEST file.

Since memtables are memory-resident, the MANIFEST only holds the live WAL files.

Writing a full version on every change is inefficient, so instead RocksDB records a base version at the beginning of the MANIFEST, and from then on it only records version edits which contain only the delta between the new version and the previous one and on startup the MANIFEST is replayed to get the current version.

When the MANIFEST reaches a certain size (configurable), RocksDB creates a new MANIFEST file with the current version as base.

The current MANIFEST file name is stored in the CURRENT file.

### Iterators

Used for iterating over an ordered range of keys (as opposed to a single key read).

Begins by seeking into the start of the range (calling Seek() on the iterator) to get the first key, and advancing the iterator (calling Next() on it) to get the next key.

Iterators require checking each memtable and every level of the LSM to see if the seek key falls in its range (for L0 this requires checking each file on the level).

When advancing the iterator, it needs to check all of the levels to find the next key (implemented using a min-heap).

Reverse iteration is also supported, though it may be less efficient.

\\

### Iterators and resources consumption

Just as a read returns the key as seen at the specific point in time when it was initiated, an iterator presents the view of the key-value store as it was at the specific point in time when the iteration was initiated.

To present a point-in-time view of the LSM without SST files and memtables disappearing from under it, an iterator takes a reference on the current version, keeping it alive for the duration of the iteration.

This can lead to increased disk and memory usage, especially if the write rate is high.

### Snapshots

For non-iterator use-cases, where having a view of the key-value store at a specific time point is needed, keeping a version alive is too expensive.

Instead, RocksDB allows preserving older versions of keys visible by a specific sequence number during compaction by taking a snapshot.

When trying to read from a snapshot, the snapshot should be provided as part of the read options, causing RocksDB to ignore keys with higher sequence numbers.

### Merge operators

In RMW (Read-Modify-Write) operations, such as incrementing an integer value, it's important to ensure that the value doesn't change between reading and writing (or the modification will lose information).

However, some modifications are commutative and mutually excluding them (such as doing the modification as part of a transaction) is too expensive.

RocksDB supports providing a merge operator for a column family, which allows writing a merge operand containing a value that would allow the operator to merge two or more operands into the final value.

The downside of using a merge operator is that a read might need to check all of the levels to gather all of the operands and return a value.

### Range Delete

Useful for cases where there’s a need to delete a large amount of keys in a specific range, which would require creating many deletion tombstones and sometimes also knowing which keys in the range were written.

Stores a special record which contains an end-exclusive range of keys to delete in the form \[first-key, last-key).

The range deletion records are kept in a separate structure for each memtable and SST file, and consulted at the beginning of the read from the memtable or SST file.

### Single delete

An optimisation of the deletion operation, to avoid having to propagate a deletion tombstone all the way to the bottom-most level even when no earlier versions of a key exist.

As soon as a key-value pair is encountered, a compaction discards the key-value pair and does not propagate the deletion tombstone further.

Only works when exactly one version of the key is visible in the LSM below the single delete marker (otherwise an older version of the key will resurface).

\
\\

\
\\

\\


# Performance testing

We run performance tests for several reasons:

**Dev:** This is to ensure there's no degradation due to additional code and to ensure stabilization. \
In the case of a performance feature, this type of testing ensures that we achieve the expected performance gain.

{% hint style="info" %}
Dev tests are a combination of regression tests and larger benchmarks.&#x20;
{% endhint %}

**Stress test:** By running our code over a longer period of time and under stress, we ensure there's no additional degradation. &#x20;

**Competitive analysis:** This enables us to compare our performance to our competitors and show our achievements.&#x20;

{% hint style="info" %}
For each test we run, it's important to understand the reason for running this specific test, in addition to the configuration and pattern.
{% endhint %}

### Test coverage&#x20;

When testing, we aim to get maximum coverage. This includes variations on configurations and io patterns.

### Terminology&#x20;

This page uses the below terms.&#x20;

For the configuration key size default 16B the below are value sizes.

* Small obj – 64B&#x20;
* Large obj – 1000B&#x20;
* Small DB – Below the instance RAM < 100GB (consider, smaller instance and smaller DB )
* Large DB – Above the instance RAM > 150GB&#x20;
* Huge DB – Factor larger than large
* db\_bench benchmark, or benchmark – a set of tests running on a specific configuration
* Test - a single result within a benchmark

### Performance instance&#x20;

* type i3.4xlarge&#x20;
* Intel(R) Xeon(R) CPU E5-2686 v4 @ 2.30GHz&#x20;
* 16 cores&#x20;
* RAM 122GB

### Current test configurations&#x20;

* Large obj, small DB&#x20;
* Small obj small DB&#x20;
* Large obj, large DB&#x20;
* Large obj huge DB – in this configuration, multiple CF are also tested&#x20;

{% hint style="info" %}
It's possible to run tests in other configurations.
{% endhint %}

### Test IO patterns&#x20;

Currently, each benchmark includes fillup + 10 patterns:

1. Fillup 100% random writes&#x20;
2. 100% random reads&#x20;
3. 100% random write (rewrite)&#x20;
4. Mix load reads writes&#x20;
5. Seeks

{% hint style="info" %}
The tests are run in order; the state of the DB is affected by previous tests.
{% endhint %}

### Performance optimization settings

We added the following settings to our db\_bench testing:

* Reduce the CPU bottlenecks on compaction
* max\_background\_flushes=4
  * Allow better handling of faster workflows
* max\_write\_buffer\_number=4
  * Allow better handling of faster workflows
* write\_buffer\_size=265MB&#x20;
  * In some cases, improves memory handling
* bloom\_bits=10

### Performance test results

The performance benchmark result will be uploaded to a dashboard (AKA web admin). The result will also be compared to a baseline either in graphs or tables. We aim to get simple results in the form of:

* Better - Improvement with >X% across all benchmarks & tests, with memory & disk space usage the same or less&#x20;
* Same - +- X% across all benchmarks & tests, memory & disk space&#x20;
* Degraded – >X% less across all benchmarks & tests, memory & disk space same or more
* Inconclusive – Improvement in some tests and degradation in others

{% hint style="info" %}
In addition to the IOPS results, we will monitor and report the memory usage, the disk space consumption, and CPU stats (although at this stage, we don't evaluate them).
{% endhint %}

### Additional parameter tests

Running the same tests with different parameters to increase coverage.&#x20;

Suggested:

1. Compression - off/LZ4/Snappy&#x20;
2. Wal (redo log) - off/on&#x20;
3. Number of threads – 50/16/4/1&#x20;
4. Optional feature active/disabled&#x20;


