11 unstable releases (3 breaking)
new 0.4.4 | Oct 3, 2024 |
---|---|
0.4.3 | Oct 3, 2024 |
0.4.2 | Sep 23, 2024 |
0.3.2 | Sep 19, 2024 |
0.1.1 | Sep 3, 2024 |
#326 in Database implementations
7,622 downloads per month
Used in 6 crates
(3 directly)
200KB
4K
SLoC
SurrealCS Client
The SurrealCS client is a simple key-value store client that can be used to interact with the SurrealCS server.
Connections
The connection between the client and the server consists of a pool of TCP connections. Lets say we want to create a pool of 3 connections, we can do this with the following code:
use surrealcs::router::create_connection_pool;
let ids: Vec<usize> = create_connection_pool("127.0.0.1:8080", Some(3)).await.unwrap();
The create_connection_pool
function returns a Vec<usize>
which is the indexes of the connection in the pool. This index is not really needed, as allocation is done automatically, but it can be useful for debugging purposes. If the number of
connections is not specified, the default number of connections will be the number of cores on the machine.
We can also create a single connection with the following code:
use surrealcs::router::{send_to_router, create_connection};
let id = create_connection("127.0.0.1:8080", send_to_router).await.unwrap();
The create_connection
function returns a usize
which is the index of the connection in the pool. This index is not really needed, as allocation is done automatically, but it can be useful for debugging purposes. The send_to_router
is a function
that sends to the global connection allocator. You can also create your own function that handles the sending to a connection
allocator. However, you must also manage your own connection pool. This can be useful for testing purposes or if you wanted
to have different connection pools to different servers.
Strict Transactions
Strict transactions are when the functions available to the transaction handle are restricted based on the state of the transaction. We can also exploit the Rust compiler to check the state of the transaction, meaning that we can strict a function input of a transaction that only has a particular state if needed by the following:
fn some_function(transaction: &mut Transaction<NotStarted>) {
// Do something
}
fn another_function(transaction: &mut Transaction<InProgress>) {
// Do something
}
fn yet_another_function(transaction: &mut Transaction<Committed>) {
// Do something
}
fn final_function(transaction: &mut Transaction<RolledBack>) {
// Do something
}
We can start a transaction with the following code:
use surrealcs_kernel::messages::server::{
interface::ServerTransactionMessage,
kv_operations::{MessageDel, MessageGet, MessagePut},
message::{KeyValueOperationType, Transaction as TransactionMessage};
};
use surrealcs::transactions::interface::interface::{BridgeHandle, Transaction};
let transaction: Transaction<NotStarted> = Transaction::new().await.unwrap();
// begin a transaction with a put
let message = ServerTransactionMessage::Put(MessagePut {
key: b"key".to_vec(),
value: b"value".to_vec(),
});
// returns a Transaction<InProgress>
let (message, mut transaction) = transaction.begin::<BridgeHandle>(message).await.unwrap();
match outcome {
ServerTransactionMessage::EmptyResponse => {},
_ => panic!("should have been a put"),
};
// send a get message
let message = ServerTransactionMessage::Get(MessageGet {
key: b"key".to_vec(),
});
// returns a Transaction<InProgress>
let outcome = transaction.send::<BridgeHandle>(message).await.unwrap();
let outcome = match outcome {
ServerTransactionMessage::ResponseGet(outcome) => outcome,
_ => panic!("should have been a put"),
};
// commit with a get transaction
let message = ServerTransactionMessage::Get(MessageGet {
key: b"key".to_vec(),
});
// returns a Transaction<Committed>
// We also have an empty_commit function that can be used to commit a transaction without
// any additional messages
let (outcome, _transaction) = transaction.commit::<BridgeHandle>(message).await.unwrap();
It must be noted that we utilize a BridgeHandle
when sending messages to our transaction actor
associated with the handle. This handle facilitates the sending of the message to the actor and
receiving the response from the actor. We have a handle to allow users to implement their own
handles if they wish to do so. The handle also helps with unit testing to see how the transaction
handle handles multiple edgecases. If you want to implement your own handle, you can do so by
implement the surrealcs::transactions::interface::bridge::SendToTransactionClientActor
for a struct. The SendToTransactionClientActor
does not reference the state of the
struct implementing the trait.
Relaxed Transactions
Relaxed transactions are built on top of strict transactions. Please read the strict transactions section before reading this section.
Relaxed transactions are transactions that allow the user to send any message and the state does not change. To create a relaxed transaction, we can do the following:
use surrealcs::transactions::interface::interface::{
Transaction,
Any
};
let transaction: Transaction<Any> = Transaction::new().await.unwrap();
You can then perform a range of operations on the transaction without the state changing. This is useful for legacy systems that are not designed to support strict transactions.
Dependencies
~7–13MB
~158K SLoC