Files
core/test/rust/performance/job/reference.rs
T
2026-11-03 03:32:07 +00:00

157 lines
4.9 KiB
Rust

// 手写 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: dever_runtime::wire::serde::Deserializer<'input>>(
decoder: Decoder,
) -> Result<Self, Decoder::Error> {
#[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::<Fields, Decoder>(decoder)?;
Ok(Self {
name: fields.name,
amount: fields.amount,
})
}
}
struct RustResult {
_id: dever_runtime::database::Id<RESULT_MODEL>,
_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<dever_runtime::value::id::Id, dever_runtime::database::Error> {
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<Claim>,
scope: Scope<'static>,
registry: Arc<Registry>,
) -> 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
}