@@ -2,16 +2,73 @@ local zset_key = KEYS[1]
22local processed_key = KEYS [2 ]
33local owners_key = KEYS [3 ]
44local worker_queue_key = KEYS [4 ]
5+ local test_group_timeout_key = KEYS [5 ]
56
6- local current_time = ARGV [1 ]
7+ local current_time = tonumber ( ARGV [1 ])
78local test = ARGV [2 ]
9+ local default_timeout = tonumber (ARGV [3 ]) or 0
810
911-- already processed, we do not need to bump the timestamp
1012if redis .call (' sismember' , processed_key , test ) == 1 then
1113 return false
1214end
1315
14- -- we're still the owner of the test, we can bump the timestamp
15- if redis .call (' hget' , owners_key , test ) == worker_queue_key then
16- return redis .call (' zadd' , zset_key , current_time , test )
16+ -- we're still the owner of the test, check if we need to extend the deadline
17+ local owner_value = redis .call (' hget' , owners_key , test )
18+ if owner_value then
19+ -- Parse owner value: format is "worker_queue_key|initial_reservation_time|last_heartbeat_time"
20+ local first_pipe = string.find (owner_value , " |" )
21+ if not first_pipe then
22+ return false
23+ end
24+ local stored_worker_key = string.sub (owner_value , 1 , first_pipe - 1 )
25+
26+ if stored_worker_key == worker_queue_key then
27+ -- Parse initial reservation time and last heartbeat time
28+ local rest = string.sub (owner_value , first_pipe + 1 )
29+ local second_pipe = string.find (rest , " |" )
30+ local initial_reservation_time
31+ if second_pipe then
32+ initial_reservation_time = tonumber (string.sub (rest , 1 , second_pipe - 1 ))
33+ else
34+ -- Backward compatibility: old format only has one timestamp
35+ initial_reservation_time = tonumber (rest )
36+ end
37+
38+ -- Update last heartbeat timestamp in owners hash (keep initial reservation time)
39+ local new_owner_value = worker_queue_key .. " |" .. (initial_reservation_time or current_time ) .. " |" .. current_time
40+ redis .call (' hset' , owners_key , test , new_owner_value )
41+
42+ local deadline = redis .call (' zscore' , zset_key , test )
43+ if deadline then
44+ deadline = tonumber (deadline )
45+
46+ -- Get the estimated timeout for this test
47+ local estimated_timeout = redis .call (' hget' , test_group_timeout_key , test )
48+ if not estimated_timeout or estimated_timeout == " " then
49+ estimated_timeout = default_timeout
50+ else
51+ estimated_timeout = tonumber (estimated_timeout )
52+ end
53+
54+ -- Cap deadline at 3x the estimated timeout from initial reservation
55+ local max_deadline = (initial_reservation_time or current_time ) + (estimated_timeout * 3 )
56+
57+ -- Only extend if deadline is within 20 seconds of expiring
58+ if deadline - 20 < current_time then
59+ -- Extend by 1 minute, but don't exceed max deadline
60+ local new_deadline = math.min (current_time + 60 , max_deadline )
61+
62+ -- Only update if we're actually extending
63+ if new_deadline > deadline then
64+ redis .call (' zadd' , zset_key , new_deadline , test )
65+ return {deadline , new_deadline }
66+ end
67+ end
68+ end
69+ -- No extension needed, but heartbeat was recorded
70+ return 0
71+ end
1772end
73+
74+ return false
0 commit comments