Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,156 @@
|
||||
// 手写 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
|
||||
}
|
||||
Reference in new issue
Block a user