Bicycle 🚲 — A database framework

sean watters
2 min readAug 27, 2023
PCE

Introducing yet another, creative, redundant, way to do “NoSQL”

Bicycle 🚲 is a framework for defining database schemas whose access patterns are generated as code and compiled into each server binary.

We’re striving to reduce dynamic query parsing at run time.

Why the name?

  • Wheels (transport): gRPC
  • Frame (storage engine): RocksDB
  • Pedals, gears, handlebars, breaks, etc. (logic): Rust

Usage

We don’t distribute the binary yet but if you clone down this repository you can play around with it:

// schema.proto
syntax = "proto3";
package bicycle;

message Dog {
string pk = 1;

string name = 2;
uint32 age = 3;
string breed = 4;
}

That will create a server binary and proto file for your consuming services. So in the cli/out/ you'll have server and bicycle.proto.

## clone
git clone git@github.com:ordinarylabs/bicycle.git && cd bicycle

## generate your `./out/server` and `./out/bicycle.proto`
cargo run --package bicycle_cli -- create path/to/your/schema.proto

The bicycle.proto is what any developer who is familiar with gRPC can use to code-gen and build a client to the database. Right now, the database is very light weight and has no administration infrastructure, permissions or auth; I get away with this because I'm only ever running it in private subnets within the same VPCs on AWS and stuff. But there is always room for evolution.

Running

Clients

./out/server

Because the database server is just a gRPC server, you can use all native gRPC libraries for any language you like. and you can also roll over to your preferred gRPC GUI client, type in localhost::50051, AND because we implement server reflection, when you plug in the URL it will automatically load up all your available RPCs (assuming your client GUI supports that).

syntax = "proto3";
package bicycle;

message Dogs {
repeated Dog dogs = 1;
}
message Dog {
string pk = 1;
string name = 2;
uint32 age = 3;
string breed = 4;
}

message IndexQuery {
oneof expression {
string eq = 1;
string gte = 2;
string lte = 3;
string begins_with = 4;
}
}

message Empty {}

service Bicycle {
rpc GetDogsByPk(IndexQuery) returns (Dogs) {}
rpc DeleteDogsByPk(IndexQuery) returns (Empty) {}
rpc PutDog(Dog) returns (Empty) {}
rpc BatchPutDogs(Dogs) returns (Empty) {}
}

Example

And then you have the IndexQuery helper which basically allows you to do key-range queries.

  • GetXByPk
  • DeleteXByPk
  • PutX
  • BatchPutX

Here are the really basic examples:

Contributing

## PutDog
grpcurl -plaintext -d '{
"pk": "DOG#1",
"name": "Rover",
"age": 3,
"breed": "Golden Retriever"
}' localhost:50051 bicycle.Bicycle.PutDog

## BatchPutDogs
grpcurl -plaintext -d '{
"dogs": [
{
"pk": "DOG#2",
"name": "Buddy",
"age": 2,
"breed": "Labrador"
},
{
"pk": "DOG#3",
"name": "Max",
"age": 4,
"breed": "Poodle"
}
]
}' localhost:50051 bicycle.Bicycle.BatchPutDogs

## GetDogs
grpcurl -plaintext -d '{"begins_with": "DOG#"}' localhost:50051 bicycle.Bicycle.GetDogsByPk

## DeleteDogs
grpcurl -plaintext -d '{"eq": "DOG#3"}' localhost:50051 bicycle.Bicycle.DeleteDogsByPk

Originally published at https://problemchild.engineering on August 27, 2023.

--

--