Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+192
View File
@@ -0,0 +1,192 @@
//! 新场景沿用同一个 compare 构建器;只替换测试入口名,保留正式生成的认证和 Job 分派。
use dever::compiler::{emit, model::Program};
use std::path::Path;
#[path = "../job/build.rs"]
mod jobs;
pub fn run(core: &Path) -> bool {
if std::env::args().any(|value| value == "--rest-json") {
crate::compare(
core,
"auth",
"rest-json",
&(include_str!("../samples.rs").to_owned()
+ include_str!("../application/rest_json.rs")
+ "\nmod output { use super::*;\n"
+ include_str!("../application/rest_output.rs")
+ "\n}\n"),
&[("GENERATED_RAW_ENCODE", "demo.document.app.encode")],
&[],
);
return true;
}
if std::env::args().any(|value| value == "--rest-wire") {
crate::compare(
core,
"auth",
"rest-wire",
&(include_str!("../samples.rs").to_owned() + include_str!("../application/rest.rs")),
&[],
&[],
);
return true;
}
if std::env::args().any(|value| value == "--api-wire") {
crate::compare(
core,
"api-wire",
"api-wire",
&(include_str!("../samples.rs").to_owned() + include_str!("../application/http.rs")),
&[],
&[],
);
return true;
}
let http = std::env::args().any(|value| value == "--auth-http");
if http || std::env::args().any(|value| value == "--auth") {
let reference = [
include_str!("shared.rs"),
include_str!("reference.rs"),
include_str!("measure.rs"),
"mod protected_http { use super::*;\n",
include_str!("http/reference.rs"),
include_str!("http/client.rs"),
include_str!("http/check.rs"),
include_str!("http/measure.rs"),
"}\n",
&format!("const HTTP_BENCH: bool = {http};\n"),
]
.concat();
crate::compare(
core,
"auth-performance",
if http { "auth-http" } else { "auth" },
&reference,
&[
("GENERATED_SEED", "bench.account.app.seed"),
("GENERATED_PERMISSIONS", "bench.account.app.permissions"),
],
&[],
);
true
} else if std::env::args().any(|value| value == "--jobs") {
jobs::run(core);
true
} else {
false
}
}
pub fn metadata(name: &str, program: &Program, reference: &str) -> String {
match name {
"rest-json" => {
let index = program
.rest
.iter()
.position(|entry| {
program.packages
[program.definitions[program.models[entry.model].definition].source]
== "demo.document.model"
})
.unwrap();
let source = emit::application::generate(program);
let route = source
.split_once(&format!("async fn rest{index}("))
.unwrap()
.1;
let detail = route
.split_once("let row = rows.pop().ok_or(dever_runtime::api::Failure(404))?; ")
.unwrap()
.1;
let page = route
.split_once("let page = executor.page(")
.unwrap()
.1
.split_once(".await.map_err(rest_error)?; ")
.unwrap()
.1;
let definition = program.models[program.rest[index].model].definition;
reference
.replace("RAW_ARGS", &format!("RestCreate{index}"))
.replace("RAW_MODEL_ID", &definition.to_string())
.replace("RAW_MODEL", &format!("Type{definition}"))
.replace(
"REST_DETAIL_ENCODE",
detail.split_once(".map_err(Into::into)").unwrap().0,
)
.replace(
"REST_PAGE_ENCODE",
page.split_once(".map_err(Into::into)").unwrap().0,
)
}
"rest-wire" => {
// 比较实际 REST 输入类型及其解码入口,不把数据库和认证计入 codec 耗时。
let source = emit::application::generate(program);
let (_, input) = source
.split_once("let args: RestCreate0 = input.decode(")
.unwrap();
let decoder = input.split_once(')').unwrap().0;
reference.replace("REST_DECODER", decoder)
}
"api-wire" => [
("GENERATED_HTTP", "/bench/wire/echo"),
("GENERATED_LIST", "/bench/wire/count"),
]
.into_iter()
.fold(reference.to_owned(), |source, (marker, path)| {
let index = program
.http
.iter()
.position(|entry| entry.path == path && entry.method == "POST")
.unwrap();
source.replace(marker, &format!("api{index}"))
}),
"auth" | "auth-http" => {
assert_eq!(program.verifiers.len(), 1);
let verifier = &program.verifiers[0];
let account = program
.models
.iter()
.position(|model| {
program.packages[program.definitions[model.definition].source]
== "bench.account.model"
})
.unwrap();
reference
.replace("ACCOUNT_BINDING", &account.to_string())
.replace("AUTH_BINDING", &(program.models.len() + 1).to_string())
.replace("AUTH_USER_MODEL", &verifier.user.1.to_string())
.replace("AUTH_TENANT_MODEL", &verifier.tenant.1.to_string())
.replace(
"GENERATED_HTTP",
&format!(
"api{}",
program
.http
.iter()
.position(|entry| entry.path == "/bench/account/admin/permissions"
&& entry.method == "GET")
.unwrap()
),
)
}
"jobs" => jobs::metadata(program, reference),
_ => reference.into(),
}
}
pub fn application(name: &str, program: &Program) -> String {
if matches!(
name,
"auth" | "auth-http" | "jobs" | "api-wire" | "rest-wire" | "rest-json"
) {
let source = emit::application::generate(program);
assert_eq!(source.matches("fn main() {").count(), 1);
source.replacen("fn main() {", "fn application_main() {", 1)
} else if !program.commands.is_empty() {
emit::application::functions(program)
} else {
String::new()
}
}
+83
View File
@@ -0,0 +1,83 @@
// 正确性检查在计时外覆盖凭据、Provider、角色、路由及严格输入,不能用空 200 掩盖漏做。
fn check_requests(connection: &mut HttpConnection, input: &AuthInput) {
for request in &input.requests[..16] {
check_reply(&connection.request(&request_bytes(request)), 200);
}
check_reply(
&connection.request(&request_bytes(&input.requests[16])),
401,
);
let valid = &input.requests[0];
let mut missing = valid.clone();
missing.headers_mut().remove("cookie");
let mut invalid = valid.clone();
invalid
.headers_mut()
.insert("cookie", "session=v1.invalid-jwt".parse().unwrap());
let mut host = valid.clone();
host.headers_mut()
.insert("host", "other.test".parse().unwrap());
let mut path = valid.clone();
*path.uri_mut() = format!("{}-missing", path.uri()).parse().unwrap();
let mut query = valid.clone();
*query.uri_mut() = format!("{}?unexpected=1", query.uri()).parse().unwrap();
for (request, status) in [
(missing, 401),
(invalid, 401),
(host, 404),
(path, 404),
(query, 400),
] {
check_reply(&connection.request(&request_bytes(&request)), status);
}
}
fn membership(runtime: &Runtime, registry: &Arc<Registry>, input: &AuthInput, grant: bool) {
runtime
.run(async |scope| {
let (mut context, claims) = input
.service
.prepare("admin", &input.requests[0], false, input.now)
.unwrap();
rust_verify(&mut context, claims.unwrap(), scope, registry)
.await
.map_err(|error| error.0)
.unwrap();
let mut database = DbContext::new(registry.clone());
let executor = database.on(AUTH_BINDING, scope).await.unwrap();
if grant {
authorization::grant(executor, scope, &context, 16, "reader")
.await
.unwrap();
} else {
authorization::revoke(executor, scope, &context, 16, "reader")
.await
.unwrap();
}
})
.unwrap();
}
fn check(
runtime: &Runtime,
registry: &Arc<Registry>,
config: &Arc<api::Config>,
input: &AuthInput,
) {
for generated in [true, false] {
with_server(runtime, registry, config, input, generated, |port| {
check_requests(&mut HttpConnection::connect(port), input);
});
}
membership(runtime, registry, input, false);
for generated in [true, false] {
with_server(runtime, registry, config, input, generated, |port| {
let mut connection = HttpConnection::connect(port);
check_reply(
&connection.request(&request_bytes(&input.requests[15])),
403,
);
});
}
membership(runtime, registry, input, true);
}
+91
View File
@@ -0,0 +1,91 @@
// 客户端只连接本轮持有的回环监听器,使用同一 HTTP/1.1 长连接顺序发送请求。
use std::{
io::{BufRead, BufReader, Read, Write},
net::TcpStream,
time::Duration,
};
const ROUTE: &str = "/bench/account/admin/permissions";
const CONTENT_TYPE: &str = "application/json; charset=utf-8";
const SUCCESS: &str = "{\"code\":0,\"message\":\"ok\",\"data\":[{\"key\":\"bench.account.admin.permissions\",\"component\":\"bench\",\"domain\":\"account\",\"site\":\"admin\",\"action\":\"permissions\",\"method\":\"GET\",\"kind\":\"http\"}]}";
struct HttpReply {
status: u16,
content_type: String,
body: Vec<u8>,
}
struct HttpConnection(BufReader<TcpStream>);
impl HttpConnection {
fn connect(port: u16) -> Self {
let socket = TcpStream::connect(("127.0.0.1", port)).unwrap();
socket.set_nodelay(true).unwrap();
socket
.set_read_timeout(Some(Duration::from_secs(5)))
.unwrap();
socket
.set_write_timeout(Some(Duration::from_secs(5)))
.unwrap();
Self(BufReader::new(socket))
}
fn request(&mut self, request: &[u8]) -> HttpReply {
self.0.get_mut().write_all(request).unwrap();
let mut line = String::new();
self.0.read_line(&mut line).unwrap();
assert!(line.starts_with("HTTP/1.1 "));
let status = line.split_whitespace().nth(1).unwrap().parse().unwrap();
let mut length = None;
let mut content_type = String::new();
loop {
line.clear();
assert!(self.0.read_line(&mut line).unwrap() > 0);
if line == "\r\n" {
break;
}
let (name, value) = line.trim_end().split_once(':').unwrap();
match name.to_ascii_lowercase().as_str() {
"content-length" => {
assert!(length.is_none());
length = Some(value.trim().parse::<usize>().unwrap());
}
"content-type" => content_type = value.trim().into(),
"connection" => assert_ne!(value.trim(), "close"),
"transfer-encoding" | "set-cookie" => panic!("unexpected response header"),
_ => (),
}
}
let mut body = vec![0; length.unwrap()];
self.0.read_exact(&mut body).unwrap();
HttpReply {
status,
content_type,
body,
}
}
}
fn request_bytes(request: &Request) -> Vec<u8> {
assert!(request.body().is_empty());
let mut bytes = format!("{} {} HTTP/1.1\r\n", request.method(), request.uri()).into_bytes();
for (name, value) in request.headers() {
bytes.extend_from_slice(name.as_str().as_bytes());
bytes.extend_from_slice(b": ");
bytes.extend_from_slice(value.as_bytes());
bytes.extend_from_slice(b"\r\n");
}
bytes.extend_from_slice(b"Connection: keep-alive\r\nContent-Length: 0\r\n\r\n");
bytes
}
fn check_reply(reply: &HttpReply, status: u16) {
assert_eq!(reply.status, status);
assert_eq!(reply.content_type, CONTENT_TYPE);
let expected = if status == 200 {
SUCCESS.as_bytes().to_vec()
} else {
api::error(status).body().to_vec()
};
assert_eq!(reply.body, expected);
}
+123
View File
@@ -0,0 +1,123 @@
// 两侧复用相同 HTTP 引擎、限额和调度策略;计时包含真实 JWT 校验、三次数据库读取及响应。
fn with_server<Value>(
runtime: &Runtime,
registry: &Arc<Registry>,
config: &Arc<api::Config>,
input: &AuthInput,
generated: bool,
client: impl FnOnce(u16) -> Value,
) -> Value {
let registry = registry.clone();
let config = config.clone();
let authentication = input.service.clone();
std::thread::scope(|threads| {
let (started, port) = std::sync::mpsc::sync_channel(0);
let (finished, completion) = tokio::sync::oneshot::channel();
let serving = threads.spawn(move || {
runtime
.run(async move |scope| {
let listener = dever_runtime::net::listen("127.0.0.1", 0, scope)
.await
.unwrap();
let port = listener.port().unwrap() as u16;
let incoming = listener.clone();
let server = scope
.spawn(move |child| async move {
let limits = dever_runtime::http::Limits {
connections: 1,
..Default::default()
};
api::serve::<_, _, true>(
&incoming,
&limits,
&child,
None,
move |request, scope, peer| {
let config = config.clone();
let registry = registry.clone();
let authentication = authentication.clone();
async move {
let path = request.uri().path();
let response = match (request.method().as_str(), path) {
("GET", ROUTE) if config.accepts("admin", &request) => {
if generated {
GENERATED_HTTP(
&request,
peer,
&scope,
&registry,
&authentication,
)
.await
} else {
rust_http(
&request,
peer,
&scope,
&registry,
&authentication,
)
.await
}
}
_ => api::error(404),
};
Ok(response)
}
},
)
.await
})
.unwrap();
// 阻塞客户端独立于运行时线程,也不占用数据库的阻塞池;交接在计时外。
started.send(port).unwrap();
completion.await.unwrap();
listener.close().unwrap();
match server.timeout(5000).await.unwrap() {
dever_runtime::task::Timed::Ready(result) => result.unwrap(),
_ => panic!("owned HTTP benchmark server failed to drain"),
}
})
.unwrap()
});
let result = client(port.recv().unwrap());
finished.send(()).unwrap();
serving.join().unwrap();
result
})
}
pub(super) fn run(
runtime: &Runtime,
registry: &Arc<Registry>,
config: &Arc<api::Config>,
input: &AuthInput,
) {
check(runtime, registry, config, input);
eprintln!(
"HTTP/1.1 loopback; one persistent connection; rotating 16 JWT cookies; JWT + Provider + role gate + permission list + JSON response included; TCP connect and response assertions excluded; no TLS/proxy/concurrent-load claim"
);
let requests = input.requests[..16]
.iter()
.map(request_bytes)
.collect::<Vec<_>>();
samples("http_jwt_provider_roles", |generated, count| {
with_server(runtime, registry, config, input, generated, |port| {
let mut connection = HttpConnection::connect(port);
check_reply(&connection.request(&requests[0]), 200);
let mut replies = Vec::with_capacity(count);
let start = Instant::now();
for index in 0..count {
replies.push(connection.request(black_box(&requests[index % requests.len()])));
}
let elapsed = start.elapsed().as_nanos();
let mut checksum = 0u64;
for reply in &replies {
check_reply(reply, 200);
checksum += black_box(reply.body.iter().map(|byte| u64::from(*byte)).sum::<u64>());
}
(elapsed, checksum)
})
});
check(runtime, registry, config, input);
}
@@ -0,0 +1,102 @@
// 手写 Rust 完成同样的请求边界、Provider、权限门禁和业务返回,不调用生成 API。
use dever_runtime::{api, authorization, wire};
use wire::serde::{self, Deserialize, Serialize};
#[derive(Serialize)]
#[serde(crate = "dever_runtime::wire::serde")]
struct RustPermission {
key: String,
component: String,
domain: String,
site: String,
action: String,
method: String,
kind: String,
}
#[derive(Deserialize)]
#[serde(crate = "dever_runtime::wire::serde", deny_unknown_fields)]
struct EmptyFields {}
struct EmptyInput;
impl<'input> Deserialize<'input> for EmptyInput {
fn deserialize<Decoder: serde::Deserializer<'input>>(
decoder: Decoder,
) -> Result<Self, Decoder::Error> {
let _: EmptyFields = wire::object(decoder)?;
Ok(Self)
}
}
fn access_failure(error: authorization::Failure) -> ApiFailure {
match error {
authorization::Failure::Access(status) => ApiFailure(status),
authorization::Failure::Database(_) => ApiFailure(500),
}
}
async fn rust_http(
request: &Request,
peer: std::net::SocketAddr,
scope: &Scope<'_>,
registry: &Arc<Registry>,
authentication: &Arc<auth::Service>,
) -> Response {
let now = match auth::now() {
Ok(now) => now,
Err(error) => return api::error(error.status()),
};
let (mut context, claims) = match authentication.prepare_at("admin", request, false, now, peer)
{
Ok(prepared) => prepared,
Err(error) => return api::error(error.status()),
};
if let Some(claims) = claims {
if let Err(error) = rust_verify(&mut context, claims, scope, registry).await {
return error.response();
}
}
let mut database = DbContext::new(registry.clone());
let outcome = async {
authorization::authorize(
&mut database,
AUTH_BINDING,
scope,
&context,
"bench",
"account",
"permissions",
)
.await
.map_err(access_failure)?;
let input = api::input(request).map_err(ApiFailure)?;
let _: EmptyInput = input
.decode(dever_runtime::wire::decode)
.map_err(|_| ApiFailure(400))?;
let values = authorization::permissions(&mut database, AUTH_BINDING, scope, &context, true)
.await
.map_err(access_failure)?
.into_iter()
.map(|value| RustPermission {
key: value.key,
component: value.component,
domain: value.domain,
site: value.site,
action: value.action,
method: value.method,
kind: value.kind,
})
.collect::<Vec<_>>();
wire::response(&values).map_err(ApiFailure::from)
}
.await;
match outcome {
Ok(body) => {
let mut response = api::response(body);
context.publish(&mut response);
response
}
Err(error) => error.response(),
}
}
+297
View File
@@ -0,0 +1,297 @@
// JWT 解析在计时前完成;本场景测 Provider 数据库复核及随后实际角色读取。
use dever_runtime::api::{Request, Response};
struct AuthInput {
service: Arc<auth::Service>,
requests: Vec<Request>,
claims: Vec<Claims>,
now: i64,
}
fn prepare(path: &Path) -> (Arc<dever_runtime::api::Config>, AuthInput) {
let config = Arc::new(dever_runtime::api::Config::load(path, &["admin"]).unwrap());
let service = Arc::new(
auth::Service::new(
config.clone(),
&[auth::Verification {
name: "bench.account.verify",
user_model: AUTH_USER_MODEL,
tenant_model: AUTH_TENANT_MODEL,
}],
&[auth::Route {
topic: "admin",
required: true,
user_models: &[AUTH_USER_MODEL],
tenant_models: &[AUTH_TENANT_MODEL],
}],
)
.unwrap(),
);
let now = auth::now().unwrap();
let claims = (0..17)
.map(|index| Claims {
subject: format!("account-{index:02} 中文\""),
session: format!("session-{index}"),
site: "admin".into(),
tenant: None,
})
.collect::<Vec<_>>();
let requests = claims
.iter()
.map(|claims| {
// 只用公开 Cookie 接口准备凭据,不读取 Secret 的私有字节。
let mut bootstrap = Request::new(Default::default());
*bootstrap.method_mut() = "POST".parse().unwrap();
*bootstrap.uri_mut() = "/login".parse().unwrap();
bootstrap
.headers_mut()
.insert("host", "benchmark.test".parse().unwrap());
bootstrap
.headers_mut()
.insert("origin", "https://benchmark.test".parse().unwrap());
let (mut context, _) = service.prepare("admin", &bootstrap, true, now).unwrap();
context
.issue_cookie(&claims.subject, &claims.session, None, now)
.unwrap();
let mut response = Response::new(Default::default());
context.publish(&mut response);
let cookie = response.headers().get("set-cookie").unwrap();
let cookie = std::str::from_utf8(cookie.as_bytes())
.unwrap()
.split(';')
.next()
.unwrap()
.as_bytes()
.to_vec();
let mut request = Request::new(Default::default());
*request.uri_mut() = "/bench/account/admin/permissions".parse().unwrap();
request
.headers_mut()
.insert("host", "benchmark.test".parse().unwrap());
request
.headers_mut()
.insert("cookie", cookie.try_into().unwrap());
request
})
.collect();
(
config,
AuthInput {
service,
requests,
claims,
now,
},
)
}
fn measure_auth(
runtime: &Runtime,
registry: &Arc<Registry>,
input: &AuthInput,
generated: bool,
roles: bool,
count: usize,
) -> (u128, u64) {
runtime
.run(async |scope| {
let mut contexts = input.requests[..16]
.iter()
.map(|request| {
input
.service
.prepare("admin", request, false, input.now)
.unwrap()
.0
})
.collect::<Vec<_>>();
let mut checksum = 0u64;
let start = Instant::now();
for index in 0..count {
let position = index % contexts.len();
let context = &mut contexts[position];
verify(
generated,
context,
black_box(&input.claims[position]).clone(),
scope,
registry,
)
.await
.map_err(|error| error.0)
.unwrap();
checksum += black_box(
context.user_id(None).unwrap().unwrap() as u64
+ context.id().unwrap().len() as u64,
);
if roles {
let mut database = DbContext::new(registry.clone());
let values =
black_box(permissions(generated, context, scope, &mut database).await);
for value in &values {
for field in value {
checksum += field.bytes().map(u64::from).sum::<u64>();
}
}
}
}
(start.elapsed().as_nanos(), checksum)
})
.unwrap()
}
fn check_auth(runtime: &Runtime, registry: &Arc<Registry>, input: &AuthInput) {
runtime
.run(async |scope| {
let expected = vec![
[
"bench.account.admin.permissions",
"bench",
"account",
"admin",
"permissions",
"GET",
"http",
]
.map(String::from),
];
for generated in [true, false] {
for index in 0..16 {
let (mut context, claims) = input
.service
.prepare("admin", &input.requests[index], false, input.now)
.unwrap();
verify(generated, &mut context, claims.unwrap(), scope, registry)
.await
.map_err(|error| error.0)
.unwrap();
assert_eq!(context.id().unwrap(), input.claims[index].subject);
assert_eq!(
context.user_id(Some(AUTH_USER_MODEL)).unwrap(),
Some(index as i64 + 1)
);
assert!(
context
.tenant_id(Some(AUTH_TENANT_MODEL))
.unwrap()
.is_none()
);
let mut database = DbContext::new(registry.clone());
assert_eq!(
permissions(generated, &mut context, scope, &mut database).await,
expected
);
}
for (index, wrong_session) in [(16, false), (0, true)] {
let (mut context, claims) = input
.service
.prepare("admin", &input.requests[index], false, input.now)
.unwrap();
let mut claims = claims.unwrap();
if wrong_session {
claims.session.push_str("-revoked");
}
assert_eq!(
verify(generated, &mut context, claims, scope, registry)
.await
.unwrap_err()
.0,
401
);
assert!(context.id().is_err());
}
}
})
.unwrap();
}
fn main() {
let (directory, path) = directory(
"auth",
r#""http":{"host":"127.0.0.1","port":18001},"sites":{"admin":{"path":"admin","hosts":["benchmark.test"],"auth":"session","origin":"https://benchmark.test"}},"auth":{"providers":{"session":{"verify":"bench.account.verify","jwtSecret":"benchmark-only-key-not-for-any-deployment","ttlSeconds":3600,"cookie":"session"}}}"#,
);
let runtime = runtime();
let registry = runtime
.run(async |scope| database_registry(&path, scope).await)
.unwrap()
.unwrap();
let (config, input) = prepare(&path);
runtime
.run(async |scope| {
let mut database = DbContext::new(registry.clone());
for (index, claims) in input.claims.iter().enumerate() {
let account = GENERATED_SEED(
&claims.subject,
&claims.session,
index < 16,
scope,
&mut database,
)
.await
.unwrap();
assert_eq!(account.field0.0, index as i64 + 1);
}
let (mut context, claims) = input
.service
.prepare("admin", &input.requests[0], false, input.now)
.unwrap();
drop(database);
api_verify(&mut context, claims.unwrap(), scope, &registry)
.await
.map_err(|error| error.0)
.unwrap();
let mut database = DbContext::new(registry.clone());
let executor = database.on(AUTH_BINDING, scope).await.unwrap();
dever_runtime::authorization::synchronize(
executor,
scope,
&config,
&[dever_runtime::authorization::Declaration {
topic: "admin",
component: "bench",
domain: "account",
action: "permissions",
method: "GET",
}],
)
.await
.unwrap();
dever_runtime::authorization::save(
&mut database,
AUTH_BINDING,
scope,
&context,
"reader",
"Benchmark reader",
false,
&["bench.account.admin.permissions".into()],
)
.await
.unwrap();
for user in 1..=16 {
let executor = database.on(AUTH_BINDING, scope).await.unwrap();
dever_runtime::authorization::grant(executor, scope, &context, user, "reader")
.await
.unwrap();
}
})
.unwrap();
check_auth(&runtime, &registry, &input);
header();
if HTTP_BENCH {
protected_http::run(&runtime, &registry, &config, &input);
} else {
for (name, roles) in [
("auth_provider_verify", false),
("auth_provider_role_read", true),
] {
samples(name, |generated, count| {
measure_auth(&runtime, &registry, &input, generated, roles, count)
});
}
}
check_auth(&runtime, &registry, &input);
drop(registry);
drop(runtime);
drop(directory);
}
+114
View File
@@ -0,0 +1,114 @@
// 手写 Rust 重做 Provider 的查询、完整行解码和身份判断;不调用生成函数。
use dever_runtime::{
api::Failure as ApiFailure,
auth::{self, Claims, Identity},
};
struct RustAccount {
id: dever_runtime::database::Id<AUTH_USER_MODEL>,
_created_at: dever_runtime::time::calendar::DateTime,
subject: String,
_session: String,
_active: bool,
}
async fn rust_verify(
context: &mut auth::Context<'_>,
claims: Claims,
scope: &Scope<'_>,
registry: &Arc<Registry>,
) -> Result<(), ApiFailure> {
if context.verifier() != Some("bench.account.verify") {
return Err(ApiFailure(500));
}
let mut database = DbContext::new(registry.clone());
let executor = database
.on(ACCOUNT_BINDING, scope)
.await
.map_err(|_| ApiFailure(500))?;
let mut rows = executor.query(scope,
"SELECT \"id\",\"created_at\",\"subject\",\"session\",\"active\" FROM \"bench_account_model\" WHERE (((\"subject\" IS ?1) AND (\"session\" IS ?2)) AND (\"active\" IS ?3)) ORDER BY \"id\" DESC LIMIT 1",
vec![Parameter::Text(claims.subject), Parameter::Text(claims.session), Parameter::Integer(1)], 1,
|row| Ok(RustAccount {
id: dever_runtime::database::Id(row.get::<_, i64>(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"))?,
subject: row.get(2)?, _session: row.get(3)?, _active: row.get(4)?,
})
).await.map_err(|_| ApiFailure(500))?;
let row = rows.pop().ok_or(ApiFailure(401))?;
context
.establish(Identity {
id: row.subject,
user_id: Some(row.id.0),
tenant_id: None,
})
.map_err(|error| ApiFailure(error.status()))
}
// 输出所有字段,防止只读长度让优化器删掉 Dever 或 Rust 的记录转换。
type PermissionView = [String; 7];
async fn rust_permissions(
context: &mut auth::Context<'_>,
scope: &Scope<'_>,
database: &mut DbContext,
) -> Vec<PermissionView> {
dever_runtime::authorization::permissions(database, AUTH_BINDING, scope, context, true)
.await
.unwrap()
.into_iter()
.map(|value| {
[
value.key,
value.component,
value.domain,
value.site,
value.action,
value.method,
value.kind,
]
})
.collect()
}
async fn permissions(
generated: bool,
context: &mut auth::Context<'_>,
scope: &Scope<'_>,
database: &mut DbContext,
) -> Vec<PermissionView> {
if generated {
GENERATED_PERMISSIONS(scope, database, context)
.await
.unwrap()
.into_iter()
.map(|value| {
[
value.field0,
value.field1,
value.field2,
value.field3,
value.field4,
value.field5,
value.field6,
]
})
.collect()
} else {
rust_permissions(context, scope, database).await
}
}
async fn verify(
generated: bool,
context: &mut auth::Context<'_>,
claims: Claims,
scope: &Scope<'_>,
registry: &Arc<Registry>,
) -> Result<(), ApiFailure> {
if generated {
api_verify(context, claims, scope, registry).await
} else {
rust_verify(context, claims, scope, registry).await
}
}
+92
View File
@@ -0,0 +1,92 @@
// SQLite、资源作用域和优化参数两侧一致;只比较生成层,不推断完整语言或 HTTP 吞吐。
use dever_runtime::{
database::{Context as DbContext, Parameter, Registry},
task::{Limits, Runtime, Scope},
};
use std::{
hint::black_box,
path::{Path, PathBuf},
sync::Arc,
time::Instant,
};
struct BenchDirectory(PathBuf);
impl Drop for BenchDirectory {
fn drop(&mut self) {
std::fs::remove_dir_all(&self.0).unwrap();
}
}
fn directory(name: &str, extra: &str) -> (BenchDirectory, PathBuf) {
let unique = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let relative = format!("{name}-benchmark-{}-{unique}", std::process::id());
let directory = BenchDirectory(
std::env::current_exe()
.unwrap()
.parent()
.unwrap()
.join(&relative),
);
std::fs::create_dir(&directory.0).unwrap();
let settings = directory.0.join("setting.json");
std::fs::write(&settings, format!(r#"{{"database":{{"default":{{"type":"sqlite","path":"{relative}/case.db","max_connections":1,"max_page_size":2048}}}},{extra}}}"#)).unwrap();
(directory, settings)
}
fn runtime() -> Runtime {
Runtime::new(Limits {
threads: 1,
workers: 1,
tasks: 16,
})
.unwrap()
}
fn smoke() -> bool {
std::env::args().any(|value| value == "--smoke")
}
fn samples(name: &str, mut measure: impl FnMut(bool, usize) -> (u128, u64)) {
if let Ok(selected) = std::env::var("DEVER_BENCH_CASES")
&& !selected.split(',').any(|case| case == name)
{
return;
}
let (rounds, count) = if smoke() { (1, 4) } else { (15, 2000) };
if !smoke() {
for generated in [true, false] {
black_box(measure(generated, 200));
}
}
for sample in 0..rounds {
let mut order = [("dever", true), ("rust", false), ("control", true)];
order.rotate_left(sample % 3);
let mut expected = None;
for (implementation, generated) in order {
let (elapsed, checksum) = measure(generated, count);
if let Some(expected) = expected {
assert_eq!(checksum, expected);
}
expected = Some(checksum);
if !smoke() {
println!("{name},{sample},{implementation},{elapsed},{checksum}");
}
}
}
if smoke() {
println!("correctness smoke passed: {name}; no performance result collected");
}
}
fn header() {
eprintln!(
"Rust 1.95.0; release fat LTO; codegen-units=1; overflow-checks=true; SQLite WAL/NORMAL; one connection; one runtime worker; setup/checks excluded"
);
if !smoke() {
println!("case,sample,implementation,nanoseconds,checksum");
}
}