Skip to content

Commit 9c84cc2

Browse files
committed
filter notif earlier
1 parent d7a91a2 commit 9c84cc2

3 files changed

Lines changed: 74 additions & 96 deletions

File tree

src/error/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ pub enum HttpgError {
1313
backtrace: snafu::Backtrace,
1414
},
1515
#[snafu(transparent)]
16+
SendError {
17+
source: Box<tokio::sync::broadcast::error::SendError<tokio_postgres::Notification>>,
18+
backtrace: snafu::Backtrace,
19+
},
20+
#[snafu(transparent)]
1621
Conf {
1722
source: conf::Error,
1823
backtrace: snafu::Backtrace,

src/extract/query.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ use axum::{body::Body, http::{header::CONTENT_TYPE, Request}};
377377

378378
use axum::extract::FromRequest;
379379
use conf::Conf;
380-
use tokio_postgres::AsyncMessage;
380+
use tokio_postgres::Notification;
381381
use crate::{extract::query::{Param, Query}};
382382

383383
#[tokio::test]
@@ -394,7 +394,7 @@ use tokio_postgres::AsyncMessage;
394394

395395
let (client, mut _conn) = httpg_config.pg.connect().await.unwrap();
396396

397-
let (tx, _rx) = tokio::sync::broadcast::channel::<AsyncMessage>(16);
397+
let (tx, _rx) = tokio::sync::broadcast::channel::<Notification>(16);
398398

399399
let state = crate::AppState {
400400
read_pool,
@@ -424,7 +424,7 @@ use tokio_postgres::AsyncMessage;
424424

425425
let (client, mut _conn) = httpg_config.pg.connect().await.unwrap();
426426

427-
let (tx, _rx) = tokio::sync::broadcast::channel::<AsyncMessage>(16);
427+
let (tx, _rx) = tokio::sync::broadcast::channel::<Notification>(16);
428428

429429
let state = crate::AppState {
430430
read_pool,

src/main.rs

Lines changed: 66 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,17 @@ use lettre::{
2222
};
2323
use serde_json::json;
2424
use tokio::sync::{broadcast::Sender};
25-
use tokio_stream::wrappers::errors::BroadcastStreamRecvError;
2625
use tower::builder::ServiceBuilder;
2726
use tower_http::{cors::{Any, CorsLayer}, services::ServeDir, trace::TraceLayer};
2827
use web_push::{ContentEncoding, HyperWebPushClient, SubscriptionInfo, VapidSignatureBuilder, WebPushClient, WebPushMessageBuilder};
29-
use std::{env, fs::{self, File}, net::{SocketAddr, TcpListener}, ops::Deref, sync::Arc};
28+
use std::{env, fs::{self, File}, net::{SocketAddr, TcpListener}, sync::Arc};
3029
use std::collections::HashMap;
31-
use tokio_postgres::{AsyncMessage, Client, IsolationLevel, types::{ToSql, Type}};
32-
use deadpool_postgres::{Pool, Transaction};
30+
use tokio_postgres::{AsyncMessage, Client, IsolationLevel, Notification, types::Type};
31+
use deadpool_postgres::Pool;
3332
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
3433
use biscuit_auth::{KeyPair, PrivateKey, Biscuit, builder::*};
3534

36-
use crate::{error::HttpgError, extract::query::Query, postgres::{PostgresConfig, QueryGuard}, response::CancelStream};
35+
use crate::{error::HttpgError, postgres::{PostgresConfig, QueryGuard}, response::CancelStream};
3736

3837
#[derive(Clone, Conf)]
3938
struct TlsConfig {
@@ -79,7 +78,7 @@ struct AppState {
7978
read_pool: Pool,
8079
write_pool: Pool,
8180
config: HttpgConfig,
82-
tx: Sender<AsyncMessage>,
81+
tx: Sender<Notification>,
8382
client: Arc<Client>,
8483
}
8584

@@ -99,28 +98,21 @@ async fn main() -> Result<(), HttpgError> {
9998
let write_pool = httpg_config.pg.write_pool()?;
10099

101100
let (client, mut conn) = httpg_config.pg.connect().await?;
102-
103-
let (tx, _rx) = tokio::sync::broadcast::channel::<AsyncMessage>(16);
104-
101+
let (tx, _rx) = tokio::sync::broadcast::channel::<Notification>(16);
105102
let mut stream = futures::stream::poll_fn(move |cx| conn.poll_message(cx));
106-
107103
let wrapped_tx = tx.clone();
108104
tokio::spawn(async move {
109105
while let Some(Ok(m)) = stream.next().await {
110-
wrapped_tx.send(m).unwrap();
111-
// Event::default().data(n.payload())
112-
// )).map_err(|e| HttpgError::anyhow(e.to_string()))?;
113-
// match m {
114-
// tokio_postgres::AsyncMessage::Notice(n) => tracing::info!("{n:#?}"),
115-
// tokio_postgres::AsyncMessage::Notification(n) => {
116-
// tx.send(Ok(
117-
// Event::default().data(n.payload())
118-
// )).map_err(|e| HttpgError::anyhow(e.to_string()))?;
119-
// },
120-
// _ => {HttpgError::anyhow("unsupported AsyncMessage");},
121-
// }
106+
match m {
107+
tokio_postgres::AsyncMessage::Notice(n) => {
108+
tracing::info!("{n:#?}");
109+
},
110+
tokio_postgres::AsyncMessage::Notification(n) => {
111+
wrapped_tx.send(n).map_err(Box::new)?;
112+
}
113+
_ => {}
114+
}
122115
}
123-
124116
Ok::<_, HttpgError>(())
125117
});
126118

@@ -138,6 +130,7 @@ async fn main() -> Result<(), HttpgError> {
138130
.route("/logout", get(logout).post(logout))
139131
.route("/{path}/logout", get(logout).post(logout))
140132
.route("/sse/{channel}", get(sse_query))
133+
.route("/{path}/sse/{channel}", get(sse_query))
141134
.route("/query", get(stream_query).post(post_query))
142135
.route("/{path}/query", get(stream_query).post(post_query))
143136
.route("/{path}/query/{cursor}", post(post_query))
@@ -252,12 +245,13 @@ async fn login(
252245
let root = KeyPair::from(&PrivateKey::from_bytes(&private_key, Algorithm::Ed25519)?);
253246

254247
let mut conn = write_pool.get().await?;
255-
let mut tx = conn.build_transaction()
248+
let tx = conn.build_transaction()
256249
.isolation_level(IsolationLevel::Serializable)
257250
.start().await?
258251
;
259252

260-
let _guard = pre(&mut tx, &biscuit, &anon_role, &query).await?;
253+
tx.query_typed_raw("select set_config('httpg.query', $1, true)", [(serde_json::to_string(&query)?, Type::TEXT)]).await?;
254+
tx.batch_execute(&pre(&biscuit, &anon_role)).await?;
261255

262256
let sql_params: Vec<(_, Type)> = query.params.iter().map(|param| {
263257
(param.tosql_sync(), param.to_owned().into())
@@ -306,50 +300,15 @@ async fn logout(
306300
))
307301
}
308302

309-
async fn pre<'a>(tx: &mut Transaction<'a>, biscuit: &Option<extract::biscuit::Biscuit>, anon_role: &String, query: &'a Query) -> Result<QueryGuard, HttpgError> {
303+
fn pre(biscuit: &Option<extract::biscuit::Biscuit>, anon_role: &String) -> String {
310304

311-
let guard = QueryGuard {
312-
cancel_token: tx.cancel_token(),
313-
finished: false,
314-
};
305+
let mut s = vec![
306+
format!("set local role to {anon_role}"),
307+
];
315308

316-
tx.batch_execute(&format!("set local role to {anon_role}")).await?;
317-
318-
if let Some(lang) = &query.accept_language {
319-
let mut lang = lang.split(",").next().unwrap_or("en-US").replace("-", "_");
320-
lang.push_str(".UTF8");
321-
322-
let params: [(&(dyn ToSql + Sync), Type); 1] = [
323-
(
324-
&lang,
325-
Type::TEXT
326-
)
327-
];
328-
329-
let stx = tx.savepoint("lc_time").await?;
330-
331-
let res = stx.query_typed("select set_config('lc_time', $1, true)", &params).await;
332-
333-
match res {
334-
Ok(_) => stx.commit().await?,
335-
Err(_) => stx.rollback().await?,
336-
}
337-
}
338-
339-
tx.query_typed_raw("select set_config('httpg.query', $1, true)", vec![
340-
(
341-
serde_json::to_string(&query)?,
342-
Type::TEXT
343-
)
344-
]).await?;
309+
s.push(biscuit.to_owned().map(|b| b.0.join(";")).unwrap_or_default());
345310

346-
if let Some(extract::biscuit::Biscuit(b)) = biscuit {
347-
futures::future::join_all(b.iter().map(async |sql| {
348-
tx.batch_execute(sql).await
349-
})).await;
350-
}
351-
352-
Ok(guard)
311+
s.join(";")
353312
}
354313

355314
#[debug_handler]
@@ -360,12 +319,17 @@ async fn email(
360319
) -> Result<impl IntoResponse, HttpgError> {
361320

362321
let mut conn = write_pool.get().await?;
363-
let mut tx = conn.build_transaction()
322+
let tx = conn.build_transaction()
364323
.isolation_level(IsolationLevel::Serializable)
365324
.start().await
366325
?;
367326

368-
let _guard = pre(&mut tx, &biscuit, &anon_role, &query).await?;
327+
let _guard = QueryGuard {
328+
cancel_token: tx.cancel_token(),
329+
finished: false,
330+
};
331+
tx.query_typed_raw("select set_config('httpg.query', $1, true)", [(serde_json::to_string(&query)?, Type::TEXT)]).await?;
332+
tx.batch_execute(&pre(&biscuit, &anon_role)).await?;
369333

370334
let sql_params: Vec<(_, Type)> = query.params.iter().map(|param| {
371335
(param.tosql_sync(), param.to_owned().into())
@@ -451,12 +415,17 @@ async fn web_push(
451415
) -> Result<impl IntoResponse, HttpgError> {
452416

453417
let mut conn = read_pool.get().await?;
454-
let mut tx = conn.build_transaction()
418+
let tx = conn.build_transaction()
455419
.isolation_level(IsolationLevel::RepeatableRead)
456420
.start().await
457421
?;
458422

459-
let _guard = pre(&mut tx, &biscuit, &anon_role, &query).await?;
423+
let _guard = QueryGuard {
424+
cancel_token: tx.cancel_token(),
425+
finished: false,
426+
};
427+
tx.query_typed_raw("select set_config('httpg.query', $1, true)", [(serde_json::to_string(&query)?, Type::TEXT)]).await?;
428+
tx.batch_execute(&pre(&biscuit, &anon_role)).await?;
460429

461430
let sql_params: Vec<(_, Type)> = query.params.iter().map(|param| {
462431
(param.tosql_sync(), param.to_owned().into())
@@ -536,13 +505,20 @@ async fn stream_query(
536505
None => read_pool,
537506
}.get().await?;
538507

539-
let mut tx = conn.build_transaction()
508+
let tx = conn.build_transaction()
540509
.read_only(true)
541510
.isolation_level(IsolationLevel::RepeatableRead)
542511
.start().await?
543512
;
544513

545-
let guard = pre(&mut tx, &biscuit, &anon_role, &query).await?;
514+
let guard = QueryGuard {
515+
cancel_token: tx.cancel_token(),
516+
finished: false,
517+
};
518+
519+
tx.query_typed_raw("select set_config('httpg.query', $1, true)", [(serde_json::to_string(&query)?, Type::TEXT)]).await?;
520+
521+
tx.batch_execute(&pre(&biscuit, &anon_role)).await?;
546522

547523
let sql_params: Vec<(_, Type)> = query.params.iter().map(|param| {
548524
(param.tosql_sync(), param.to_owned().into())
@@ -573,21 +549,12 @@ async fn sse_query(
573549

574550
Ok(Sse::new(
575551
tokio_stream::wrappers::BroadcastStream::new(tx.subscribe())
576-
.try_filter_map(move |m| {
552+
.try_filter_map(move |n| {
577553
let channel = channel.clone();
578554
async move {
579-
match m {
580-
tokio_postgres::AsyncMessage::Notice(n) => {
581-
tracing::info!("{n:#?}");
582-
Ok(None)
583-
},
584-
tokio_postgres::AsyncMessage::Notification(n) => {
585-
match n.channel() {
586-
c if c == channel => Ok(Some(Event::default().data(n.payload()))),
587-
_ => Ok(None)
588-
}
589-
},
590-
_ => Err(BroadcastStreamRecvError::Lagged(1)),
555+
match n.channel() {
556+
c if c == channel => Ok(Some(Event::default().data(n.payload()))),
557+
_ => Ok(None)
591558
}
592559
}
593560
})
@@ -616,9 +583,14 @@ async fn post_query(
616583
let mut retry: u8 = 0;
617584
loop {
618585
let mut conn = write_pool.get().await?;
619-
let mut tx = conn.build_transaction().isolation_level(IsolationLevel::Serializable).start().await?;
586+
let tx = conn.build_transaction().isolation_level(IsolationLevel::Serializable).start().await?;
620587

621-
let guard = pre(&mut tx, &biscuit, anon_role, &query).await?;
588+
let guard = QueryGuard {
589+
cancel_token: tx.cancel_token(),
590+
finished: false,
591+
};
592+
tx.query_typed_raw("select set_config('httpg.query', $1, true)", [(serde_json::to_string(&query)?, Type::TEXT)]).await?;
593+
tx.batch_execute(&pre(&biscuit, anon_role)).await?;
622594

623595
let result = tx.query_typed(query.sql.as_ref().ok_or(HttpgError::anyhow("no sql passed"))?, &sql_params).await;
624596

@@ -658,19 +630,20 @@ async fn post_query(
658630
continue;
659631
}
660632

661-
662633
match &query.on_error {
663634
Some(on_error) => {
664-
let errors = json!({"error": &error});
665-
666635
let mut conn = read_pool.get().await?;
667-
let mut tx = conn.build_transaction().read_only(true).isolation_level(IsolationLevel::RepeatableRead).start().await?;
636+
let tx = conn.build_transaction().read_only(true).isolation_level(IsolationLevel::RepeatableRead).start().await?;
668637

669-
let guard = pre(&mut tx, &biscuit, anon_role, &query).await?;
638+
let guard = QueryGuard {
639+
cancel_token: tx.cancel_token(),
640+
finished: false,
641+
};
642+
tx.batch_execute(&pre(&biscuit, anon_role)).await?;
670643

671644
tx.query_typed_raw(
672645
"select set_config('httpg.errors', $1, true)",
673-
vec![(serde_json::to_string(&errors)?, Type::TEXT)]
646+
vec![(serde_json::to_string(&json!({"error": &error}))?, Type::TEXT)]
674647
).await?;
675648

676649
let rows = tx.query_typed_raw(on_error.as_ref(), sql_params).await?;

0 commit comments

Comments
 (0)