Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
/target
Justfile
Justfile
/commit.sh
/GO.sh
/DROP_DB.sh
/TODO.txt
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 39 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,65 @@

Hulykvs is a simple key-value store service implemented in Rust. It uses cockroachdb as the backend and provides a simple http api for storing and retrieving key-value pairs.

## API
## API v2
Create a key-value pair api

```POST /api2/{workspace}/{namespace}/{key}```
Stores request payload as the value for the given key in the given namespace. Existing keys will be overwritten. Returs 204 (NoContent) on sucesss.


```POST /api2/insert/{workspace}/{namespace}/{key}```
Inserts a new key-value pair. Fails if the key already exists. Returs 204 (NoContent) on sucesss.


```POST /api2/update/{workspace}/{namespace}/{key}```
Updates an existing key-value pair. Fails if the key does not exist. Returs 204 (NoContent) on sucesss.


```GET /api2/{workspace}/{namespace}/{key}```
Retrieves the value for the given key in the given namespace. Returns 404 if the key does not exist.


```DELETE /api2/{workspace}/{namespace}/{key}```
Deletes the key-value pair for the given key in the given namespace. Returns 404 if the key does not exist, 204 (NoContent) on success, 404 if the key does not exist.


```GET /api2/{workspace}/{namespace}?[prefix=<prefix>]```
Retrieves all key-value pairs in the given namespace. Optionally, a prefix can be provided to filter the results. The following structure is returned:
```json
{
"workspace": "workspace",
"namespace": "namespace",
"count": 3,
"keys": ["key1", "key2", "keyN"]
}
```
## API (old)
workspace = "defaultspace"

Create a key-value pair

```POST /api/{namespace}/{key}```
Stores request payload as the value for the given key in the given namespace. Existing keys will be overwritten. Returs 204 (NoContent) on sucesss.


```GET /api/{namespace}/{key}```
Retrieves the value for the given key in the given namespace. Returns 404 if the key does not exist.


```DELETE /api/{namespace}/{key}```
Deletes the key-value pair for the given key in the given namespace. Returns 404 if the key does not exist, 204 (NoContent) on success, 404 if the key does not exist.

```GET /api/{namespace}?[prefix=<prefix>]```
Retrieves all key-value pairs in the given namespace. Optionally, a prefix can be provided to filter the results. The following structure is returned:
```json
{
"namespace": "namespace",
"namespace": "namespace",
"count": 3,
"keys": ["key1", "key2", "keyN"]
}
```

## Running
Pre-build docker images is available at: hardcoreeng/service_hulykvs:{tag}.
Pre-build docker images is available at: hardcoreeng/service_hulykvs:{tag}.

You can use the following command to run the image locally:
```bash
Expand Down
10 changes: 10 additions & 0 deletions commmit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

clear

NAME="insert_update"

git checkout -b feature/${NAME}
git add .
git commit -m "Add /api2/insert /api2/update; fix migrate; fix uuid"
git push origin feature/${NAME}
1 change: 1 addition & 0 deletions hulykvs/hulykvs
5 changes: 4 additions & 1 deletion hulykvs_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ actix-cors = "0.7.1"
refinery = { version = "0.8.16", features = ["tokio-postgres"] }
tokio-postgres = "0.7.13"
bb8 = "0.9.0"
bb8-postgres = "0.9.0"
bb8-postgres = { version = "0.9.0", features = ["with-uuid-1"] }
md5 = "0.7.0"
jsonwebtoken = "9.3.1"
size = { version = "0.5.0", features = ["serde"] }
uuid = { version = "1.7", features = ["v4", "serde"] }
# regex = "1.10"

[[bin]]
name = "hulykvs"
path = "src/main.rs"

1 change: 1 addition & 0 deletions hulykvs_server/etc/migrations/V2__workspace_uuid.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE kvs ADD COLUMN workspace UUID NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000';
1 change: 1 addition & 0 deletions hulykvs_server/etc/migrations/V3__workspace_uuid.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE kvs ALTER PRIMARY KEY USING COLUMNS (workspace, namespace, key);
1 change: 1 addition & 0 deletions hulykvs_server/etc/migrations/V4__workspace_uuid.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE kvs ALTER COLUMN workspace DROP DEFAULT;
4 changes: 4 additions & 0 deletions hulykvs_server/etc/migrations/V5__workspace_uuid.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- ALTER TABLE kvs DROP CONSTRAINT kvs_namespace_key_key;
-- DROP INDEX kvs_namespace_key_key;
DROP INDEX kvs_namespace_key_key CASCADE;

4 changes: 4 additions & 0 deletions hulykvs_server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use std::{path::Path, sync::LazyLock};
use config::FileFormat;
use serde::Deserialize;

use uuid::Uuid;

#[derive(Deserialize, Debug)]
pub struct Config {
pub bind_port: u16,
Expand All @@ -29,6 +31,8 @@ pub struct Config {
pub db_scheme: String,

pub payload_size_limit: size::Size,

pub default_workspace_uuid: Uuid,
}

pub static CONFIG: LazyLock<Config> = LazyLock::new(|| {
Expand Down
3 changes: 3 additions & 0 deletions hulykvs_server/src/config/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ db_connection = "postgresql://[email protected]:26257/defaultdb?sslmode=disable"
db_scheme = "hulykvs"

payload_size_limit = "2mb"

default_workspace_uuid = "00000000-0000-0000-0000-000000000000"

27 changes: 15 additions & 12 deletions hulykvs_server/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
// limitations under the License.
//

use crate::config::CONFIG;

use actix_web::{
HttpResponse, error,
web::{self, Data, Json, Query},
Expand All @@ -39,10 +41,10 @@ pub async fn get(
let connection = pool.get().await?;

let statement = r#"
select value from kvs where namespace=$1 and key=$2
select value from kvs where workspace=$1 and namespace=$2 and key=$3
"#;

let result = connection.query(statement, &[&nsstr, &keystr]).await?;
let result = connection.query(statement, &[&CONFIG.default_workspace_uuid, &nsstr, &keystr]).await?;

let response = match result.as_slice() {
[] => HttpResponse::NotFound().finish(),
Expand Down Expand Up @@ -76,16 +78,16 @@ pub async fn post(
let md5 = md5::compute(&body);

let statement = r#"
insert into kvs(namespace, key, md5, value)
values($1, $2, $3, $4)
on conflict(namespace, key)
insert into kvs(workspace, namespace, key, md5, value)
values($1, $2, $3, $4, $5)
on conflict(workspace, workspace, namespace, key)
do update set
md5=excluded.md5,
value=excluded.value
"#;

connection
.execute(statement, &[&nsstr, &keystr, &&md5[..], &&body[..]])
.execute(statement, &[&CONFIG.default_workspace_uuid, &nsstr, &keystr, &&md5[..], &&body[..]])
.await?;

Ok(HttpResponse::NoContent().finish())
Expand All @@ -97,6 +99,7 @@ pub async fn post(
})
}


pub async fn delete(
path: ObjectPath,
pool: Data<Pool>,
Expand All @@ -111,10 +114,10 @@ pub async fn delete(
let connection = pool.get().await?;

let statement = r#"
delete from kvs where namespace=$1 and key=$2
delete from kvs where workspace=$1 and namespace=$2 and key=$3
"#;

let response = match connection.execute(statement, &[&nsstr, &keystr]).await? {
let response = match connection.execute(statement, &[&CONFIG.default_workspace_uuid, &nsstr, &keystr]).await? {
1 => HttpResponse::NoContent(),
0 => HttpResponse::NotFound(),
_ => panic!("multiple rows deleted, unique constraint is probably violated"),
Expand Down Expand Up @@ -157,16 +160,16 @@ pub async fn list(
let response = if let Some(prefix) = &query.prefix {
let pattern = format!("{}%", prefix);
let statement = r#"
select key from kvs where namespace=$1 and key like $2
select key from kvs where workspace=$1 and namespace=$2 and key like $3
"#;

connection.query(statement, &[&nsstr, &pattern]).await?
connection.query(statement, &[&CONFIG.default_workspace_uuid,&nsstr, &pattern]).await?
} else {
let statement = r#"
select key from kvs where namespace=$1
select key from kvs where workspace=$1 and namespace=$2
"#;

connection.query(statement, &[&nsstr]).await?
connection.query(statement, &[&CONFIG.default_workspace_uuid,&nsstr]).await?
};

let count = response.len();
Expand Down
Loading