Files
core/test/rust/native/application/job/heartbeat.rs
T
2026-11-03 03:32:07 +00:00

99 lines
3.7 KiB
Rust

use super::*;
use dever_runtime::{
database::{Database, Options},
task::{Limits, Runtime},
};
#[test]
#[ignore = "requires explicit isolated config/setting.json database.postgres_test"]
fn postgres_native_job_renews_short_lease_during_business_transaction() {
let directory = Directory::new();
let schema = super::super::database::postgres::Schema::new(
&directory.0,
"dever_native_job_heartbeat",
"CREATE SCHEMA dever_native_job_heartbeat",
"DROP SCHEMA dever_native_job_heartbeat CASCADE",
);
let project = Project::load(&fixture().with_file_name("job")).unwrap();
let program = project.check().unwrap();
assert_eq!(program.models[0].table, "demo_queue_model");
let binary = build::build(
&program,
&project.sources,
"main",
&directory.0.join("heartbeat"),
)
.unwrap();
let mut connection = schema.connection.clone();
connection["max_connections"] = 4.into();
std::fs::create_dir(directory.0.join("config")).unwrap();
std::fs::write(
directory.0.join("config/setting.json"),
serde_json::to_vec(&serde_json::json!({
"database":{"default":connection},
"job":{"workers":2,"poll_ms":10,"lease_ms":200,"retry_base_ms":10,"retry_max_ms":100},
"runtime":{"shutdown_ms":1000}
}))
.unwrap(),
)
.unwrap();
let run = |name: &str, input: &str| {
let result = execute(&binary, &format!("demo.queue.{name}"), input);
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
serde_json::from_slice::<serde_json::Value>(&result.stdout).unwrap()["data"].clone()
};
run("submit", r#"{"name":"slow","key":"heartbeat"}"#);
let runtime = Runtime::new(Limits::default()).unwrap();
let database = runtime.run(async |scope| {
let options: Options = serde_json::from_value(connection).unwrap();
let database = Database::open(&directory.0, options, scope).await.unwrap();
database.executor().await.unwrap().initialize(scope, "CREATE FUNCTION slow_job() RETURNS trigger LANGUAGE plpgsql AS 'BEGIN PERFORM pg_sleep(0.6); RETURN NEW; END'; CREATE TRIGGER slow_job BEFORE INSERT ON demo_queue_model FOR EACH ROW EXECUTE FUNCTION slow_job()").await.unwrap();
database
}).unwrap();
let started = Instant::now();
let mut worker = Server(Command::new(&binary).spawn().unwrap());
while run("count", "{}") != 1 {
assert!(
worker.0.try_wait().unwrap().is_none(),
"worker exited before completion"
);
assert!(
started.elapsed() < Duration::from_secs(5),
"short lease job did not complete"
);
std::thread::sleep(Duration::from_millis(20));
}
assert!(started.elapsed() >= Duration::from_millis(500));
runtime
.run(async |scope| {
let rows = database
.executor()
.await
.unwrap()
.query(
scope,
"SELECT attempt,state FROM _dever_job",
vec![],
1,
|row| Ok((row.get::<_, i64>(0)?, row.get::<_, String>(1)?)),
)
.await
.unwrap();
assert_eq!(rows, vec![(1, "completed".into())]);
})
.unwrap();
assert!(
Command::new("sh")
.args(["-c", "kill -TERM \"$1\"", "--", &worker.0.id().to_string()])
.status()
.unwrap()
.success()
);
assert!(worker.0.wait().unwrap().success());
database.close();
}