Expand description
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
Table
using column names. - update
- Create an update for a given
Table
using column names.
Structs§
- Activation
Result - Represents the result of a recipe activation.
- Controller
Handle - A handle to a Noria controller.
- Table
- A
Table
is used to perform writes, deletes, and other operations to data in base tables. - View
- A
View
is used to query previously defined external views. - Zookeeper
Authority - Coordinator that shares connection information between workers and clients using ZooKeeper.
Enums§
- Data
Type - 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.
- Table
Operation - 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.