Darin Gordon
Entrepreneur
RustNYC Meetup
Darin Gordon
September 26, 2018
Database development often plays a significant role
How can we work with Postgres in Rust?
Guided Tour of How I work with Postgres in Rust
Practices / Techniques adopted
Lessons Learned
Transitioning to Rust from Python
Bulk of the talk: Code Review and Discussion
Viable options for Postgres dev in Rust
Transition Plan
Working with Rust, full-time since Jan 2018
Worked extensively with Python prior to 2018
First: Learn Rust Foundations
Second: Learn by Porting
Viable Options
Low ROI: Beyond the basics, you're using raw sql + params
(I'm not against ORM. I just won't be using one.. yet)
Libraries: rust-postgres , rusqlite, ...
Writing SQL and running it safely with Rust
Includes many required features
(r2d2_postgres)
(tokio-postgres)
let conn = Connection::connect("postgres://user:pass@host:port/db",
TlsMode::None)?;
let updates = conn.execute("UPDATE foo SET bar = $1 WHERE baz = $2",
&[&my_int, &"bla"])?;
for row in &conn.query("SELECT bar, baz FROM foo WHERE buz = $1", &[&an_int])? {
let bar: i32 = row.get(0);
let baz: String = row.get("baz");
println!("bar: {}, baz: {}", bar, baz);
}
let stmt = conn.prepare("UPDATE foo SET bar = $1 WHERE baz = $2")?;
for (bar, baz) in updates {
stmt.execute(&[bar, baz])?;
}A conference room meeting scheduler
(unnest)
Schedule a conf room
Check room availability for preferred timeslots
"Schedule a meeting for 9-11am today in conf room 2C in the Admin building
"Check whether conf room 2C in the Admin building is available at these timeslots: [...]
Schedule a conf room
Check room availability for preferred timeslots
Work Involved
Too much to cover today
(read the code on GitHub)
have conference
Rooms
schedules a
Room
for a
Meeting
cannot be scheduled for more
than 1
Meeting
per time slot
CREATE TABLE testing.users (
id BIGSERIAL PRIMARY KEY,
ext_id UUID NOT NULL DEFAULT uuid_generate_v4(),
first_name VARCHAR(200) NOT NULL,
last_name VARCHAR(200) NOT NULL,
username VARCHAR(50) UNIQUE NOT NULL,
UNIQUE (first_name, last_name)
);
#[derive(Debug, Clone)]
pub struct User {
pub id: i64,
pub ext_id: Uuid,
pub first_name: String,
pub last_name: String,
pub username: String,
}
pub fn add_user(first_name: String,
last_name: String,
username: String,
logger: &Logger,
tx: &Transaction)
-> Result<User, MyError> {
let stmt = "
INSERT INTO testing.users(first_name, last_name, username)
VALUES ($1, $2, $3)
RETURNING testing.users.id,
testing.users.ext_id,
testing.users.first_name,
testing.users.last_name,
testing.users.username;";(part 1)
continued on next page
tx.query(stmt, &[&first_name, &last_name, &username])
.map_err(|err| {
error!(logger, "Failed to add user: DB Error.";
"step"=>"add_user", "err"=>err.to_string());
MyError::DBError(DBError::PGError(err))
})(part 2)
continued on next page
.and_then(|rows: Rows| {
rows.into_iter()
.next()
.map(|row: Row| {
// example of referencing row elements by index
let user = User { id: row.get(0),
ext_id: row.get(1),
first_name: row.get(2),
last_name: row.get(3),
username: row.get(4), };
info!(logger, "Added user: {}", user.username);
user
})
ok_or_else(|| {
error!(logger, "Error adding user to db: No record returned.";
"step"=>"add_user");
MyError::DBError(DBError::NoRecord)
})
})(part 3)
end of function
Lessons Learned
Solutions to address parallel testing issues
cargo test -- --nocapture --test-threads=1Take Advantage of Rust's Concurrency
Just Because You Can Use Postgres to Do Things, It Doesn't Mean You Should
Rust is worth the effort
https://github.com/Dowwie/pg_example
By Darin Gordon