Getting Started with ReductStore in Rust
Tags: rust, learning, reducststore, database
Published: 2024-07-04 10:35:31

This quick start guide will walk you through the process of installing and using the [**ReductStore Rust Client SDK**](https://github.com/reductstore/reduct-rs) to read and write data to a [**ReductStore**](https://www.reduct.store/) instance.

## Installing the Rust SDK[](https://www.reduct.store/docs/next/getting-started/with-rust#installing-the-rust-sdk)

To start using the ReductStore Rust SDK, add the following dependency to your `Cargo.toml` file:

```toml
[dependencies]
reduct-rs = "1"
```

Then, import the `reduct-rs` crate in your Rust code:

## Running ReductStore[](https://www.reduct.store/docs/next/getting-started/with-rust#running-reductstore)

If you don't already have a ReductStore instance running, you can easily spin up one as a Docker container. To do this, run the following command:

```
docker run -p 8383:8383 -e RS_API_TOKEN="my-token" reduct/store:latest
```

This will start a ReductStore instance listening on port 8383 on your local machine. The `RS_API_TOKEN` environment variable is used to authenticate requests to the ReductStore instance. You can set it to any value you like, but you will need to use the same value when creating a `Client` object in your code.

If Docker is not an option, you can also download the ReductStore binaries. Check the [**Download Page**](https://www.reduct.store/download).

## Hello World Example[](https://www.reduct.store/docs/next/getting-started/with-rust#hello-world-example)

Now when you have the SDK installed and a ReductStore instance running, you can start using the SDK to interact with the ReductStore database. Here is an example of using the SDK to perform basic operations on a ReductStore instance:

```rust
use bytes::Bytes;
use futures_util::stream::StreamExt;
use reduct_rs::{QuotaType, ReductClient, ReductError};
use std::pin::pin;
use std::time::{Duration, SystemTime};
use tokio;

#[tokio::main]
async fn main() -> Result<(), ReductError> {
    // 1. Create a ReductStore client
    let client = ReductClient::builder()
        .url("http://127.0.0.1:8383")
        .api_token("my-token")
        .build();

    // 2. Get or create a bucket with 1Gb quota
    let bucket = client
        .create_bucket("my-bucket")
        .quota_type(QuotaType::FIFO)
        .quota_size(1_000_000_000)
        .exist_ok(true)
        .send()
        .await?;

    // 3. Write some data with timestamps in the 'sensor-1' entry
    let start = SystemTime::now();
    bucket
        .write_record("sensor-1")
        .data(Bytes::from_static(b"Record #1"))
        .timestamp(start)
        .send()
        .await?;

    bucket
        .write_record("sensor-1")
        .data(Bytes::from_static(b"Record #2"))
        .timestamp(start + Duration::from_secs(1))
        .send()
        .await?;

    // 4. Query the data by time range
    let query = bucket
        .query("sensor-1")
        .start(start)
        .stop(start + Duration::from_secs(2))
        .send()
        .await?;

    let mut query = pin!(query);
    while let Some(record) = query.next().await {
        let record = record?;
        println!("Record timestamp: {:?}", record.timestamp());
        println!("Record size: {}", record.content_length());
        println!("{:?}", record.bytes().await?);
    }

    // 5. Exit
    Ok(())
}
```

Let's break down what this example is doing.

## Creating a Client[](https://www.reduct.store/docs/next/getting-started/with-rust#creating-a-client)

Before you can interact with a ReductStore instance, you must create a `Client` object that represents a connection to the ReductStore instance.

To create a ReductStore client, you can use the `ReductClient::builder()` method from the `reduct_rs` crate to create a builder instance. You must pass the URL of the ReductStore server as an argument to the `ClientBuilder::new()` method. Then, call the `build()` method on the builder instance to create a `Client` instance:

```rust
let client = ReductClient::builder()
    .url("http://127.0.0.1:8383")
    .api_token("my-token")
    .build();
```

## Creating a Bucket[](https://www.reduct.store/docs/next/getting-started/with-rust#creating-a-bucket)

ReductStore organizes data into buckets, each of which has its own quota and settings. It's a necessary step to create a bucket before writing data to it. You can read more about buckets in the [**Buckets Guide**](https://www.reduct.store/docs/next/guides/buckets), but for now, let's just create one.

To create a bucket, you should use the `create_bucket` method on a `Client` instance. The method takes the name of the bucket you want to create as an argument and returns a `BucketBuilder` instance to configure the bucket settings. Call the `send` method on the `BucketBuilder` to send the request to the ReductStore server and create the bucket:

```rust
let bucket = client
    .create_bucket("my-bucket")
    .quota_type(QuotaType::FIFO)
    .quota_size(1_000_000_000)
    .exist_ok(true)
    .send()
    .await?;
```

In this example we create a bucket with a FIFO quota of 1GB. This means that the oldest data will be deleted when the bucket reaches 1GB.

## Data Ingestion[](https://www.reduct.store/docs/next/getting-started/with-rust#data-ingestion)

Time series data is stored in entries within a bucket. An entry is a collection of records with unique timestamps. It must have a unique name within the bucket and usually represents a data source, such as [**a vibration sensor**](https://www.reduct.store/use-cases/vibration-sensors) or [**a CV camera**](https://www.reduct.store/blog/tutorials/computer-vision/3-ways-stora-data-for-computer-vision-applications).

To write a timestamped record to an entry in a bucket, you should use the `write_record` method on a `Bucket` instance. Pass the name of the entry you want to write to as an argument. The method will return a `RecordBuilder` instance to pass the data and timestamp you want to write. Call the `send` method on the `RecordBuilder` to send the request to the ReductStore server and write the record:

```rust
let start = SystemTime::now();
bucket
    .write_record("sensor-1")
    .data(Bytes::from_static(b"Record #1"))
    .timestamp(start)
    .send()
    .await?;

bucket
    .write_record("sensor-1")
    .data(Bytes::from_static(b"Record #2"))
    .timestamp(start + Duration::from_secs(1))
    .send()
    .await?;
```

This is the simplest case of writing data using the Python SDK. You can also write data in chunks and annotate records with labels. You can find more information and examples in the [**Data Ingestion Guide**](https://www.reduct.store/docs/next/guides/data-ingestion).

## Data Querying[](https://www.reduct.store/docs/next/getting-started/with-rust#data-querying)

Usually, we don't read a particular record by its timestamp, but query records in a time range.

To iterate over all records in a given time range, you should use the `query` method on a bucket instance. Pass the name of the entry to iterate over. The method will return a `QueryBuilder` instance to specify the time interval. Call the `send` method on the `QueryBuilder` to send the request to the ReductStore server and return a stream of records:

```rust
let query = bucket
    .query("sensor-1")
    .start(start)
    .stop(start + Duration::from_secs(2))
    .send()
    .await?;

let mut query = pin!(query);
while let Some(record) = query.next().await {
    let record = record?;
    println!("Record timestamp: {:?}", record.timestamp());
    println!("Record size: {}", record.content_length());
    println!("{:?}", record.bytes().await?);
}
```

The query method has many parameters for filtering and returning sample records. For more information and examples, see the [**Data Querying Guide**](https://www.reduct.store/docs/next/guides/data-querying).

## Next Steps[](https://www.reduct.store/docs/next/getting-started/with-rust#next-steps)

As you can see to get started with the Client SDK is very easy. However,it doesn't cover all the features of the SDK and the database. Check our [**Guides**](https://www.reduct.store/docs/next/guides) to learn more about the ReductStore features and how to use them.