// 手写 Rust 重新实现 payload 编码、业务计算、行解码和事务边界;仅复用同一队列和数据库设施。 use dever_runtime::{ job::{self, Claim, Execution, Failure as JobFailure}, wire::serde::{Deserialize, Serialize}, }; const SCHEMA: &str = JOB_SCHEMA; const TARGET: &str = "bench.queue.job.publish"; #[derive(Serialize)] #[serde(crate = "dever_runtime::wire::serde")] struct RustPayload { name: String, amount: i64, } impl<'input> Deserialize<'input> for RustPayload { fn deserialize>( decoder: Decoder, ) -> Result { #[derive(Deserialize)] #[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)] struct Fields { name: String, amount: i64, } // 与 Dever record 一样只接受对象,不能用 serde 默认的数组兼容路径降低校验成本。 let fields = dever_runtime::wire::object::(decoder)?; Ok(Self { name: fields.name, amount: fields.amount, }) } } struct RustResult { _id: dever_runtime::database::Id, _created_at: dever_runtime::time::calendar::DateTime, _name: String, amount: i64, } async fn rust_submit( name: &str, amount: i64, key: &str, scope: &Scope<'_>, database: &mut DbContext, execution: &Execution, ) -> Result { let payload = dever_runtime::wire::json::Json::encode(&RustPayload { name: name.to_owned(), amount, }) .map_err(|_| { dever_runtime::database::Error::new( dever_runtime::database::Kind::InvalidData, "invalid Job payload", ) })?; let now = dever_runtime::database::now()?; let executor = database.on(JOB_BINDING, scope).await?; job::enqueue( executor, scope, job::Submission { target: TARGET, payload: Some(&payload), schema: SCHEMA, execution, key, policy: job::Policy { retries: 2, timeout_ms: 1000, }, at: now, }, now, ) .await } async fn rust_apply( payload: RustPayload, scope: &Scope<'_>, database: &mut DbContext, ) -> Result<(), JobFailure> { let amount = payload .amount .checked_mul(3) .and_then(|value| value.checked_add(7)) .ok_or(JobFailure::Handler)?; let parameters = vec![ Parameter::Text(payload.name), Parameter::Integer(amount), Parameter::Integer(dever_runtime::database::now().map_err(|_| JobFailure::Handler)?), ]; let executor = database .on(0, scope) .await .map_err(|_| JobFailure::Handler)?; let saved = executor.query(scope, "INSERT INTO \"bench_queue_model\" (\"name\",\"amount\",\"created_at\") VALUES (?1,?2,?3) RETURNING \"id\",\"created_at\",\"name\",\"amount\"", parameters, 1, |row| Ok(RustResult { _id: dever_runtime::database::Id(row.get(0)?), _created_at: dever_runtime::time::calendar::DateTime::from_millis(row.get::<_, i64>(1)?).map_err(|_| dever_runtime::database::Error::new(dever_runtime::database::Kind::InvalidData, "invalid stored DateTime"))?, _name: row.get(2)?, amount: row.get(3)?, }) ).await.map_err(|_| JobFailure::Handler)?.pop().ok_or(JobFailure::Handler)?; if saved.amount < 0 { return Err(JobFailure::Handler); } Ok(()) } async fn rust_execute( binding: usize, claim: Arc, scope: Scope<'static>, registry: Arc, ) -> Result<(), JobFailure> { let execution: Execution = dever_runtime::wire::decode(&claim.execution).map_err(|_| JobFailure::Blocked)?; if claim.target != TARGET || claim.schema != SCHEMA || !registry.same_pool(binding, JOB_BINDING) { return Err(JobFailure::Blocked); } if !matches!(execution, Execution::System { tenant: None }) || claim.execution_tenant != 0 { return Err(JobFailure::Blocked); } let payload: RustPayload = dever_runtime::wire::decode(&claim.payload).map_err(|_| JobFailure::Blocked)?; let mut database = DbContext::new(registry); let owner = database.enter(); let outcome = async { rust_apply(payload, &scope, &mut database).await?; let executor = database .on(JOB_BINDING, &scope) .await .map_err(|_| JobFailure::Handler)?; job::complete( executor, &scope, &claim, dever_runtime::database::now().map_err(|_| JobFailure::Handler)?, ) .await .map_err(|_| JobFailure::Handler) } .await; database .finish(&scope, owner, outcome.is_ok()) .await .map_err(|_| JobFailure::Handler)?; outcome }