[−][src]Crate noria
This create contains client bindings for Noria.
What is Noria?
Noria is a new streaming data-flow system designed to act as a fast storage backend for read-heavy web applications based on this paper from OSDI'18. It acts like a databases, but pre-computes and caches relational query results so that reads are blazingly fast. Noria automatically keeps cached results up-to-date as the underlying data, stored in persistent base tables change. Noria uses partially-stateful data-flow to reduce memory overhead, and supports dynamic, runtime data-flow and query change.
Infrastructure
Like most databases, Noria follows a server-client model where many clients connect to a
(potentially distributed) server. The server in this case is the noria-server
binary, and must be started before clients can connect. See noria-server --help
for details
and the Noria repository README for details. Noria uses
Apache ZooKeeper to announce the location of its servers, so
ZooKeeper must also be running.
Quickstart example
If you just want to get up and running quickly, here's some code to dig into. Note that this requires a nightly release of Rust to run for the time being.
#[tokio::main] async fn main() { let zookeeper_addr = "127.0.0.1:2181"; let mut db = ControllerHandle::from_zk(zookeeper_addr).await.unwrap(); // if this is the first time we interact with Noria, we must give it the schema db.install_recipe(" CREATE TABLE Article (aid int, title varchar(255), url text, PRIMARY KEY(aid)); CREATE TABLE Vote (aid int, uid int); ").await.unwrap(); // we can then get handles that let us insert into the new tables let mut article = db.table("Article").await.unwrap(); let mut vote = db.table("Vote").await.unwrap(); // let's make a new article let aid = 42; let title = "I love Soup"; let url = "https://pdos.csail.mit.edu"; article .insert(vec![aid.into(), title.into(), url.into()]) .await .unwrap(); // and then vote for it vote.insert(vec![aid.into(), 1.into()]).await.unwrap(); // we can also declare views that we want want to query db.extend_recipe(" VoteCount: \ SELECT Vote.aid, COUNT(uid) AS votes \ FROM Vote GROUP BY Vote.aid; QUERY ArticleWithVoteCount: \ SELECT Article.aid, title, url, VoteCount.votes AS votes \ FROM Article LEFT JOIN VoteCount ON (Article.aid = VoteCount.aid) \ WHERE Article.aid = ?;").await.unwrap(); // and then get handles that let us execute those queries to fetch their results let mut awvc = db.view("ArticleWithVoteCount").await.unwrap(); // looking up article 42 should yield the article we inserted with a vote count of 1 assert_eq!( awvc.lookup(&[aid.into()], true).await.unwrap(), vec![vec![DataType::from(aid), title.into(), url.into(), 1.into()]] ); }
Client model
Noria accepts a set of parameterized SQL queries (think prepared statements), and produces a data-flow program that maintains materialized views for the output of those queries. Reads now become fast lookups directly into these materialized views, as if the value had been directly read from a cache (like memcached). The views are automatically kept up-to-date by Noria through the data-flow.
Reads work quite differently in Noria compared to traditional relational databases. In
particular, a query, or view, must be registered before it can be executed, much like SQL
prepared statements. Use ControllerHandle::extend_recipe
to register new base tables and
views. Once a view has been registered, you can get a handle that lets you execute the
corresponding query by passing the view's name to ControllerHandle::view
. The returned
View
can be used to query the view with different values for its declared parameters
(values in place of ?
in the query) through View::lookup
and View::multi_lookup
.
Writes are fairly similar to those in relational databases. To add a new table, you extend the
recipe (using ControllerHandle::extend_recipe
) with a CREATE TABLE
statement, and then
use ControllerHandle::table
to get a handle to the new base table. Base tables support
similar operations as SQL tables, such as Table::insert
, Table::update
,
Table::delete
, and also more esoteric operations like Table::insert_or_update
.
Alternatives
Noria provides a MySQL adapter that implements the binary MySQL protocol, which provides a compatibility layer for applications that wish to continue to issue ad-hoc MySQL queries through existing MySQL client libraries.
Modules
debug | Types used when debugging Noria. |
error | Noria errors. |
prelude | The prelude contains most of the types needed in everyday operation. |
results | Wrapper types for Noria query results. |
Macros
row | Create a new row for insertion into a |
update | Create an update for a given |
Structs
ActivationResult | Represents the result of a recipe activation. |
ControllerHandle | A handle to a Noria controller. |
Table | A |
View | A |
ZookeeperAuthority | Coordinator that shares connection information between workers and clients using ZooKeeper. |
Enums
DataType | The main type used for user data throughout the codebase. |
Modification | A modification to make to a column in an existing row. |
Operation | A modification to make to an existing value. |
TableOperation | An operation to apply to a base table. |
Functions
trace_ops_in | The next Noria read or write issued from the current thread will be traced using tokio-trace. |