Files
windmill/backend/tests/worker_ping_ip.rs
T
Ruben Fiszelandwindmill-internal-app[bot] 588d8463df feat: let a job see that its worker is draining (#11612)
* feat: let a job see that its worker is draining

* fix: gate worker draining check on job visibility and export sdk helper

* fix: keep draining across re-registration and ignore parked jobs

* fix: reset draining only on worker startup registration

* chore: update ee-repo-ref to 55feb2e84332179faa4e17aeb419f7f480df79a6

This commit updates the EE repository reference after PR #857 was merged in windmill-ee-private.

Previous ee-repo-ref: e5010fa046a3aba93595d1bf44eb6a0cef2b5273

New ee-repo-ref: 55feb2e84332179faa4e17aeb419f7f480df79a6

Automated by sync-ee-ref workflow.

---------

Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-10-08 12:56:33 +02:00

123 lines
3.7 KiB
Rust

use sqlx::{Pool, Postgres};
use uuid::Uuid;
use windmill_common::{
external_ip::UNKNOWN_IP,
worker::{insert_ping_query, set_worker_draining_query, update_worker_ping_main_loop_query},
};
async fn insert_ping(db: &Pool<Postgres>, worker: &str, ip: Option<&str>) -> anyhow::Result<()> {
register(db, worker, ip, None).await
}
async fn register(
db: &Pool<Postgres>,
worker: &str,
ip: Option<&str>,
draining: Option<bool>,
) -> anyhow::Result<()> {
insert_ping_query(
"test-instance",
worker,
"default",
ip,
&[],
None,
None,
"test",
None,
None,
None,
false,
draining,
db,
)
.await?;
Ok(())
}
/// A worker re-registers mid-life once its external IP resolves. That registration must not
/// clear a draining flag the same process already published; only a worker starting up under
/// a reclaimed name resets it.
#[sqlx::test]
async fn only_a_starting_worker_resets_draining(db: Pool<Postgres>) -> anyhow::Result<()> {
let draining = || async {
sqlx::query_scalar::<_, bool>("SELECT draining FROM worker_ping WHERE worker = 'wk-drain'")
.fetch_one(&db)
.await
};
register(&db, "wk-drain", None, Some(false)).await?;
set_worker_draining_query("wk-drain", &db).await?;
register(&db, "wk-drain", Some("1.2.3.4"), None).await?;
assert!(draining().await?);
register(&db, "wk-drain", None, Some(false)).await?;
assert!(!draining().await?);
Ok(())
}
/// The external IP resolves in the background, so the initial ping often has none yet. That must
/// not blank the address a previous process wrote to the row this one reclaims — worker names are
/// stable across restarts under EXIT_AFTER_N_JOBS.
#[sqlx::test]
async fn unresolved_ip_keeps_the_reclaimed_rows_address(db: Pool<Postgres>) -> anyhow::Result<()> {
insert_ping(&db, "wk-reclaimed", Some("1.2.3.4")).await?;
insert_ping(&db, "wk-reclaimed", None).await?;
let ip: String = sqlx::query_scalar("SELECT ip FROM worker_ping WHERE worker = $1")
.bind("wk-reclaimed")
.fetch_one(&db)
.await?;
assert_eq!(ip, "1.2.3.4");
insert_ping(&db, "wk-fresh", None).await?;
let ip: String = sqlx::query_scalar("SELECT ip FROM worker_ping WHERE worker = $1")
.bind("wk-fresh")
.fetch_one(&db)
.await?;
assert_eq!(ip, UNKNOWN_IP);
Ok(())
}
async fn main_loop_ping(
db: &Pool<Postgres>,
worker: &str,
last_job: Option<(Uuid, &str)>,
) -> anyhow::Result<()> {
update_worker_ping_main_loop_query(
worker,
&[],
None,
None,
Some(1),
None,
None,
None,
None,
None,
None,
false,
None,
last_job,
db,
)
.await
}
/// The main-loop ping sends a job only once, so most pings carry none. Those must keep the last
/// job the row holds, whether this ping or the job poller wrote it.
#[sqlx::test]
async fn main_loop_ping_without_a_job_keeps_the_last_one(db: Pool<Postgres>) -> anyhow::Result<()> {
insert_ping(&db, "wk-last-job", None).await?;
let job_id = Uuid::new_v4();
main_loop_ping(&db, "wk-last-job", Some((job_id, "admins"))).await?;
main_loop_ping(&db, "wk-last-job", None).await?;
let (current_job_id, workspace_id): (Option<Uuid>, Option<String>) = sqlx::query_as(
"SELECT current_job_id, current_job_workspace_id FROM worker_ping WHERE worker = $1",
)
.bind("wk-last-job")
.fetch_one(&db)
.await?;
assert_eq!(current_job_id, Some(job_id));
assert_eq!(workspace_id.as_deref(), Some("admins"));
Ok(())
}