Skip to content

Commit

Permalink
Add CI for checking markdown format. (#279)
Browse files Browse the repository at this point in the history
Signed-off-by: ChenYing Kuo <[email protected]>
  • Loading branch information
evshary authored Dec 18, 2024
1 parent be60168 commit 45f49d2
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 71 deletions.
11 changes: 10 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ jobs:
- name: Run tests
run: cargo test --verbose

markdown_lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: DavidAnson/markdownlint-cli2-action@v18
with:
config: '.markdownlint.yaml'
globs: '**/README.md'

# NOTE: In GitHub repository settings, the "Require status checks to pass
# before merging" branch protection rule ensures that commits are only merged
# from branches where specific status checks have passed. These checks are
Expand All @@ -68,7 +77,7 @@ jobs:
ci:
name: CI status checks
runs-on: ubuntu-latest
needs: build
needs: [build, markdown_lint]
if: always()
steps:
- name: Check whether all jobs pass
Expand Down
6 changes: 6 additions & 0 deletions .markdownlint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"MD013": false, # Line length limitation
"MD033": false, # Enable Inline HTML
"MD041": false, # Allow first line heading
"MD045": false, # Allow Images have no alternate text
}
162 changes: 92 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

# Eclipse Zenoh

The Eclipse Zenoh: Zero Overhead Pub/sub, Store/Query and Compute.

Zenoh (pronounce _/zeno/_) unifies data in motion, data at rest and computations. It carefully blends traditional pub/sub with geo-distributed storages, queries and computations, while retaining a level of time and space efficiency that is well beyond any of the mainstream stacks.

Check the website [zenoh.io](http://zenoh.io) and the [roadmap](https://github.com/eclipse-zenoh/roadmap) for more detailed information.

-------------------------------

# RocksDB backend

In zenoh a backend is a storage technology (such as DBMS, time-series database, file system...) alowing to store the
Expand All @@ -23,70 +25,75 @@ See the [zenoh documentation](http://zenoh.io/docs/manual/backends/) for more de
This backend relies on [RocksDB](https://rocksdb.org/) to implement the storages.
Its library name (without OS specific prefix and extension) that zenoh will rely on to find it and load it is **`zenoh_backend_rocksdb`**.

:point_right: **Install latest release:** see [below](#How-to-install-it)
:point_right: **Install latest release:** see [below](#how-to-install-it)

:point_right: **Build "main" branch:** see [below](#How-to-build-it)
:point_right: **Build "main" branch:** see [below](#how-to-build-it)

-------------------------------

## **Examples of usage**

Prerequisites:
- You have a zenoh router (`zenohd`) installed, and the `zenoh_backend_rocksdb` library file is available in `~/.zenoh/lib`.
- Declare the `ZENOH_BACKEND_ROCKSDB_ROOT` environment variable to the directory where you want the RocksDB databases
to be stored. If you don't declare it, the `~/.zenoh/zenoh_backend_rocksdb` directory will be used.

- You have a zenoh router (`zenohd`) installed, and the `zenoh_backend_rocksdb` library file is available in `~/.zenoh/lib`.
- Declare the `ZENOH_BACKEND_ROCKSDB_ROOT` environment variable to the directory where you want the RocksDB databases
to be stored. If you don't declare it, the `~/.zenoh/zenoh_backend_rocksdb` directory will be used.

You can setup storages either at zenoh router startup via a configuration file, either at runtime via the zenoh admin space, using for instance the REST API.

### **Setup via a JSON5 configuration file**

- Create a `zenoh.json5` configuration file containing:
```json5
{
plugins: {
// configuration of "storages" plugin:
storage_manager: {
volumes: {
// configuration of a "rocksdb" volume (the "zenoh_backend_rocksdb" backend library will be loaded at startup)
rocksdb: {}
},
storages: {
// configuration of a "demo" storage using the "rocksdb" volume
demo: {
// the key expression this storage will subscribes to
key_expr: "demo/example/**",
// this prefix will be stripped from the received key when converting to database key.
// i.e.: "demo/example/a/b" will be stored as "a/b"
strip_prefix: "demo/example",
volume: {
id: "rocksdb",
// the RocksDB database will be stored in this directory (relative to ${ZENOH_BACKEND_ROCKSDB_ROOT})
dir: "example",
// create the RocksDB database if not already existing
create_db: true
}
- Create a `zenoh.json5` configuration file containing:

```json5
{
plugins: {
// configuration of "storages" plugin:
storage_manager: {
volumes: {
// configuration of a "rocksdb" volume (the "zenoh_backend_rocksdb" backend library will be loaded at startup)
rocksdb: {}
},
storages: {
// configuration of a "demo" storage using the "rocksdb" volume
demo: {
// the key expression this storage will subscribes to
key_expr: "demo/example/**",
// this prefix will be stripped from the received key when converting to database key.
// i.e.: "demo/example/a/b" will be stored as "a/b"
strip_prefix: "demo/example",
volume: {
id: "rocksdb",
// the RocksDB database will be stored in this directory (relative to ${ZENOH_BACKEND_ROCKSDB_ROOT})
dir: "example",
// create the RocksDB database if not already existing
create_db: true
}
}
},
// Optionally, add the REST plugin
rest: { http_port: 8000 }
}
}
},
// Optionally, add the REST plugin
rest: { http_port: 8000 }
}
```
- Run the zenoh router with:
`zenohd -c zenoh.json5`
}
```

- Run the zenoh router with:
`zenohd -c zenoh.json5`

### **Setup at runtime via `curl` commands on the admin space**

- Run the zenoh router, with write permissions to its admin space and with the REST plugin:
`zenohd --adminspace-permissions=rw --rest-http-port=8000`
- Add the "rocksdb" backend (the "zenoh_backend_rocksdb" library will be loaded):
`curl -X PUT -H 'content-type:application/json' -d '{}' http://localhost:8000/@/router/local/config/plugins/storage_manager/volumes/rocksdb`
- Add the "demo" storage using the "rocksdb" backend:
`curl -X PUT -H 'content-type:application/json' -d '{key_expr:"demo/example/**",strip_prefix:"demo/example",volume: {id: "rocksdb",dir: "example",create_db: true}}' http://localhost:8000/@/router/local/config/plugins/storage_manager/storages/demo`
- Run the zenoh router, with write permissions to its admin space and with the REST plugin:
`zenohd --adminspace-permissions=rw --rest-http-port=8000`
- Add the "rocksdb" backend (the "zenoh_backend_rocksdb" library will be loaded):
`curl -X PUT -H 'content-type:application/json' -d '{}' http://localhost:8000/@/router/local/config/plugins/storage_manager/volumes/rocksdb`
- Add the "demo" storage using the "rocksdb" backend:
`curl -X PUT -H 'content-type:application/json' -d '{key_expr:"demo/example/**",strip_prefix:"demo/example",volume: {id: "rocksdb",dir: "example",create_db: true}}' http://localhost:8000/@/router/local/config/plugins/storage_manager/storages/demo`

### **Tests using the REST API**

Using `curl` to publish and query keys/values, you can:

```bash
# Put values that will be stored in the RocksDB database
curl -X PUT -d "TEST-1" http://localhost:8000/demo/example/test-1
Expand All @@ -97,64 +104,75 @@ curl http://localhost:8000/demo/example/**
```

-------------------------------

## Volume-specific storage configuration

Storages relying on a RocksDB-backed volume must specify some additional configuration as shown [above](#setup-via-a-json5-configuration-file):

- **`"dir"`** (**required**, string) : The name of directory where the RocksDB database is stored.
The absolute path will be `${ZENOH_BACKEND_ROCKSDB_ROOT}/<dir>`.

- **`"create_db"`** (optional, boolean) : create the RocksDB database if not already existing. Not set by default.
*(the value doesn't matter, only the property existence is checked)*
_(the value doesn't matter, only the property existence is checked)_

- **`"read_only"`** (optional, boolean) : the storage will only answer to GET queries. It will not accept any PUT or DELETE message, and won't put anything in RocksDB database. Not set by default. *(the value doesn't matter, only the property existence is checked)*
- **`"read_only"`** (optional, boolean) : the storage will only answer to GET queries. It will not accept any PUT or DELETE message, and won't put anything in RocksDB database. Not set by default. _(the value doesn't matter, only the property existence is checked)_

- **`"on_closure"`** (optional, string) : the strategy to use when the Storage is removed. There are 2 options:
- *unset*: the database remains untouched (this is the default behaviour)
- _unset_: the database remains untouched (this is the default behaviour)
- `"destroy_db"`: the database is destroyed (i.e. removed)

-------------------------------

## **Behaviour of the backend**

### Mapping to RocksDB database

Each **storage** will map to a RocksDB database stored in directory: `${ZENOH_BACKEND_ROCKSDB_ROOT}/<dir>`, where:
* `${ZENOH_BACKEND_ROCKSDB_ROOT}` is an environment variable that could be specified before zenoh router startup.
If this variable is not specified `${ZENOH_HOME}/zenoh_backend_rocksdb` will be used
(where the default value of `${ZENOH_HOME}` is `~/.zenoh`).
* `<dir>` is the `"dir"` property specified at storage creation.
Each zenoh **key/value** put into the storage will map to 2 **key/values** in the database:
* For both, the database key is the zenoh key, stripped from the `"strip_prefix"` property specified at storage creation.
* In the `"default"` [Column Family](https://github.com/facebook/rocksdb/wiki/Column-Families) the key is
put with the zenoh encoded value as a value.
* In the `"data_info"` [Column Family](https://github.com/facebook/rocksdb/wiki/Column-Families) the key is
put with a bytes buffer encoded in this order:
- the Timestamp encoded as: 8 bytes for the time + 16 bytes for the HLC ID
- a "is deleted" flag encoded as a boolean on 1 byte
- the encoding prefix flag encoded as a ZInt (variable length)
- the encoding suffix encoded as a String (string length as a ZInt + string bytes without ending `\0`)

- `${ZENOH_BACKEND_ROCKSDB_ROOT}` is an environment variable that could be specified before zenoh router startup.
If this variable is not specified `${ZENOH_HOME}/zenoh_backend_rocksdb` will be used
(where the default value of `${ZENOH_HOME}` is `~/.zenoh`).
- `<dir>` is the `"dir"` property specified at storage creation.
ch zenoh **key/value** put into the storage will map to 2 **key/values** in the database:
- For both, the database key is the zenoh key, stripped from the `"strip_prefix"` property specified at storage creation.
- In the `"default"` [Column Family](https://github.com/facebook/rocksdb/wiki/Column-Families) the key is
put with the zenoh encoded value as a value.
- In the `"data_info"` [Column Family](https://github.com/facebook/rocksdb/wiki/Column-Families) the key is
put with a bytes buffer encoded in this order:
- the Timestamp encoded as: 8 bytes for the time + 16 bytes for the HLC ID
- a "is deleted" flag encoded as a boolean on 1 byte
- the encoding prefix flag encoded as a ZInt (variable length)
- the encoding suffix encoded as a String (string length as a ZInt + string bytes without ending `\0`)

### Behaviour on deletion

On deletion of a key, the corresponding key is removed from the `"default"` Column Family. An entry with the
"deletion" flag set to true and the deletion timestamp is inserted in the `"data-info"` Column Family
(to avoid re-insertion of points with an older timestamp in case of un-ordered messages).
At regular interval, a task cleans-up the `"data-info"` Column Family from entries with old timestamps and
the "deletion" flag set to true

### Behaviour on GET

On GET operations:
* if the selector is a unique key (i.e. not containing any `'*'`): the value and its encoding and timestamp
for the corresponding key are directly retrieved from the 2 Column Families using `get` RocksDB operation.
* if the selector is a key expression: the storage searches for matching keys, leveraging RocksDB's [Prefix Seek](https://github.com/facebook/rocksdb/wiki/Prefix-Seek) if possible to minimize the number of entries to check.

- if the selector is a unique key (i.e. not containing any `'*'`): the value and its encoding and timestamp
for the corresponding key are directly retrieved from the 2 Column Families using `get` RocksDB operation.
- if the selector is a key expression: the storage searches for matching keys, leveraging RocksDB's [Prefix Seek](https://github.com/facebook/rocksdb/wiki/Prefix-Seek) if possible to minimize the number of entries to check.

-------------------------------

## How to install it

To install the latest release of this backend library, you can do as follows:

### Manual installation (all platforms)

All release packages can be downloaded from:
- https://download.eclipse.org/zenoh/zenoh-backend-rocksdb/latest/

Each subdirectory has the name of the Rust target. See the platforms each target corresponds to on https://doc.rust-lang.org/stable/rustc/platform-support.html
- [https://download.eclipse.org/zenoh/zenoh-backend-rocksdb/latest/](https://download.eclipse.org/zenoh/zenoh-backend-rocksdb/latest/)

Each subdirectory has the name of the Rust target. See the platforms each target corresponds to on [https://doc.rust-lang.org/stable/rustc/platform-support.html](https://doc.rust-lang.org/stable/rustc/platform-support.html)

Choose your platform and download the `.zip` file.
Unzip it in the same directory than `zenohd` or to any directory where it can find the backend library (e.g. /usr/lib or ~/.zenoh/lib)
Expand All @@ -170,40 +188,44 @@ sudo apt install zenoh-backend-rocksdb
```

-------------------------------

## How to build it

> :warning: **WARNING** :warning: : Zenoh and its ecosystem are under active development. When you build from git, make sure you also build from git any other Zenoh repository you plan to use (e.g. binding, plugin, backend, etc.). It may happen that some changes in git are not compatible with the most recent packaged Zenoh release (e.g. deb, docker, pip). We put particular effort in mantaining compatibility between the various git repositories in the Zenoh project.
At first, install [Clang](https://clang.llvm.org/) and [Cargo and Rust](https://doc.rust-lang.org/cargo/getting-started/installation.html). If you already have the Rust toolchain installed, make sure it is up-to-date with:

```bash
$ rustup update
```
rustup update
```

> :warning: **WARNING** :warning: : As Rust doesn't have a stable ABI, the backend library should be
built with the exact same Rust version than `zenohd`, and using for `zenoh` dependency the same version (or commit number) than 'zenohd'.
Otherwise, incompatibilities in memory mapping of shared types between `zenohd` and the library can lead to a `"SIGSEV"` crash.

To know the Rust version you're `zenohd` has been built with, use the `--version` option.
Example:

```bash
$ zenohd --version
The zenoh router v0.6.0-beta.1 built with rustc 1.64.0 (a55dd71d5 2022-09-19)
```

Here, `zenohd` has been built with the rustc version `1.64.0`.
Install and use this toolchain with the following command:

```bash
$ rustup default 1.64.0
rustup default 1.64.0
```

And `zenohd` version corresponds to an un-released commit with id `1f20c86`. Update the `zenoh` dependency in Cargo.lock with this command:

```bash
$ cargo update -p zenoh --precise 1f20c86
cargo update -p zenoh --precise 1f20c86
```

Then build the backend with:

```bash
$ cargo build --release --all-targets
cargo build --release --all-targets
```

0 comments on commit 45f49d2

Please sign in to comment.