Posts from this year

Questions or comments? Email me.

Fri 10 May 2024

Pagination in Async Diesel

In the Rust ecosystem, there are quite a few ways to approach interacting with Postgres. One of the older and more established libraries is diesel, which effectively gives you a Rust-wrapped path to constructing and executing queries. If it helps, you can think of it as a "light" ORM.

One of the bigger questions over the years has been whether diesel not being "async by default" is an issue. You could always wrap your queries in tasks and just await those, and that's probably fine - in fact, if I understand correctly, this is what crates.io does. However, diesel-async also exists for those who want to have a "pure" approach to the async interaction pattern. It's a smaller crate that effectively just requires importing a few extra traits to "override" the core diesel traits, and changing your connection type to be an asynchronous one. Overall, I've found it works pretty well.

One thing I did run into recently was needing some pagination for an admin interface I was throwing together. diesel actually has an example for this that works well if you're using synchronous calls or wrapping in tasks, but if you're using diesel-async, you'll need to tweak some things to make it work. I figured I'd share my changes here in case anyone needs them; yes, these could be on GitHub, but I'm in a weird phase of re-evaluating my usage of the platform, so I'm dumping it here.

You're smart, you can figure it out. The only real changes needed were importing some diesel-async items and tweaking the bounds on load_and_count_pages to accomodate the async differences. With this, you should be able to just call .paginate(page) on your queries and have mostly automatic paging. Enjoy.

use diesel::pg::Pg;
use diesel::prelude::*;
use diesel::query_builder::*;
use diesel::sql_types::BigInt;

// Import these, since we'll need them.
use diesel_async::AsyncPgConnection;
use diesel_async::methods::LoadQuery;

pub trait Paginate: Sized {
    fn paginate(self, page: i64) -> Paginated<Self>;
}

impl<T> Paginate for T {
    fn paginate(self, page: i64) -> Paginated<Self> {
        Paginated {
            query: self,
            per_page: DEFAULT_PER_PAGE,
            page,
            offset: (page - 1) * DEFAULT_PER_PAGE,
        }
    }
}

const DEFAULT_PER_PAGE: i64 = 10;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct Paginated<T> {
    query: T,
    page: i64,
    per_page: i64,
    offset: i64,
}

impl<T> Paginated<T> {
    pub fn per_page(self, per_page: i64) -> Self {
        Paginated {
            per_page,
            offset: (self.page - 1) * per_page,
            ..self
        }
    }

    // Mark the bounds on `T` and `U` to ensure the async calls compile correctly,
    // and have `results` go through the async `RunQueryDsl`.
    pub async fn load_and_count_pages<'a, U>(
        self,
        conn: &mut AsyncPgConnection
    ) -> QueryResult<(Vec<U>, i64)>
    where
        T: 'a,
        U: Send + 'a,
        Self: LoadQuery<'a, AsyncPgConnection, (U, i64)>,
    {
        let per_page = self.per_page;
        let results = diesel_async::RunQueryDsl::load::<(U, i64)>(self, conn).await?;
        let total = results.first().map(|x| x.1).unwrap_or(0);
        let records = results.into_iter().map(|x| x.0).collect();
        let total_pages = (total as f64 / per_page as f64).ceil() as i64;
        Ok((records, total_pages))
    }
}

impl<T: Query> Query for Paginated<T> {
    type SqlType = (T::SqlType, BigInt);
}

impl<T> RunQueryDsl<PgConnection> for Paginated<T> {}

impl<T> QueryFragment<Pg> for Paginated<T>
where
    T: QueryFragment<Pg>,
{
    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
        out.push_sql("SELECT *, COUNT(*) OVER () FROM (");
        self.query.walk_ast(out.reborrow())?;
        out.push_sql(") t LIMIT ");
        out.push_bind_param::<BigInt, _>(&self.per_page)?;
        out.push_sql(" OFFSET ");
        out.push_bind_param::<BigInt, _>(&self.offset)?;
        Ok(())
    }
}
Mon 15 January 2024

Plant-Based Berlin Recommendations

2023 has come and gone, and with it, a decent chunk of travel. The list below is an assortment of plant-based/vegan options in and around Berlin, Germany. I visited most of these during a recent trip, but never compiled them into a list until recently. I figure they're as good as anything for content to update this old still creaking along site - and it beats writing about tech for the millionth time.

Eat

  • Dervish
    Vegan Uzbek cuisine. In a nightlife hotspot and open decently late, so you could do either lunch or dinner here. We never needed to make a reservation, and there are a number of other vegan restaurants on the stretch should they be full. It's not the best thing in the world, but it is cool just because it's pretty rare to find a vegan place that does this.
  • Frea Bakery / Frea Restaurant
    Zero-waste concept, all vegan. The bakery is two or so blocks from the restaurant, both are worth going to. The bakery is great for working out of if you're in need of space. Sanctuary - a vegan italian bakery - is around the corner, but if I had to choose one to go to, it'd probably be Frea. If you want the fine dining here but can't grab a booking, Cookies Cream or Lucky Leek are good second options.
  • Vöner - doner kebab
    The original "vegan kebab" and pretty much still the best. I tried quite a few alternatives before finally coming here and wish I had just come here to begin with. Some reviews say the staff are rude or whatever, but I wound up talking to them for 15 minutes and they were super chill - YMMV but I wouldn't be put off by it.
  • Chay Long P-Berg / Chay Long Hasenheide
    Vegan Vietnamese with two locations. The menu is slightly different depending on which location you go to; we went to the Hasenheide location since it has a Bún Cá Hải Phòng on the menu that we hadn't seen elsewhere. I remember liking it, though my wife said it was good but not perfect. It's kind of a tough one to vegan-ize though.
  • Tsu Tsu Kaarage
    We found this while walking around, pleasantly surprised since it gets close enough to what you'd want from kaarage. Small place with a few seats. There's also a pie shop around the corner from here that serves Oatly soft serve, but the name escapes me unfortunately.
  • Kimchi Princess KBBQ
    KBBQ spot that - surprisingly - has two entirely vegan options. We tried the seitan one and it's decent enough, and it is grilled in front of you, but they seem to not want you to handle the grill yourself which annoys me to no end. That said, I feel like finding this kind of thing with vegan options is already hard, so it was nice to experience again. They have tables for 2 if you don't want to be at a long table with others who grill meat or anything.
  • la petite véganerie - brasserie
    This one is newer and we found it while looking for vegan steak. They basically try to properly recreate a French cafe style experience and have various cheeses/steaks/etc to pull it off. If you're looking into trying some plant-based steak options that we don't have in the USA - or are hard to get over here - this place can be fun. When we went the service was slower than I can possibly put into words, but it just seemed like growing pains and the staff were really chill. You can technically walk from here to Becketts Kopf if you want cocktails after.
  • Tbilisi
    Georgian restaurant that has vegan options. We wanted to go here as several friends recommended it, but it unfortunately got screwed schedule-wise. Listing it mostly on recs from others and for variety.
  • Försters - Das Vegane Restaurant
    Because it's Berlin and you probably want some German food at some point. The website for this place is like... useless, but the food is decent and it's got massive portions for schnitzel. They also have options for the type of schnitzel, ranging from soy-based to... something else. I forget what. (Every list I read says to go to Vaust, but when we tried to go there they were really standoff-ish and said they weren't open anymore... well before their closed time... with no sign of an event going on. YMMV though.)

(I did want to add in some easy vegan breakfast spots here, but my two go-tos - No Milk Today and Cafe Nullpunkt - unfortunately shuttered at the end of 2023. That said, most coffee shops I've been in there have some vegan option nowadays due to how widespread the concept is.)

Drink

Hope you enjoy your clothes reaking of smoke. For some of the below, reservations are a good idea.

  • Timber Doodle is a casual cocktail bar around the corner from Dervish that, if it's not too crowded, has some creative cocktails and good energy.
  • Bar Becketts Kopf
    Yes, it's on the world's 50 best bars list or whatever. It's legitimately good for cocktail stuff though, and the wall art inside is... well, surreal. Pretty sure the door is unmarked.
  • Buck and Breck
    Also on 50 best list but drinks are legitimately good. No sign or anything so another unmarked bar, phones banned inside.
  • Velvet
    Another cocktail bar but tends to focus more on seasonal/local ingredients. Near-ish to the Chay Long Hasenheide location if you want to stack them. We wandered in on an industry night and somehow got seated, so dunno what the normal menu is - but the vibe is great.
  • Protokoll
    Craft beer options in Berlin generally suck but this place is decent with variety and vibe.
  • Motif
    Natural wine bar + record shop. Out of the way, but if you're in the area it's cool to know about. Should have vegan wines but you should of course ask.

Clubs

  • ://about blank
    Industrial club that is larger than it seems. Indoor area with rotating genres but usually house, outdoor chill area, back building that rotates genres like 70s remixes or local artists. The back building is either really great or really "where the actual fuck am I" experience-wise, but the club overall is great albeit out of the way. They're strict about phone use, don't be a dick to the bouncers.

Shop

  • Veganz grocery store.
    It's easy to find - right off a large train station - and you can't miss it. Fun to walk through if you like seeing what other vegan options are available outside the USA.

Ryan around the Web