Skip to content

Commit 60459e8

Browse files
committed
fix(core): fix underflow bug with desired slot count
1 parent 6124784 commit 60459e8

File tree

9 files changed

+55
-11
lines changed

9 files changed

+55
-11
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

out/openapi.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/common/types/src/keys/pegboard/ns.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ impl ServerlessDesiredSlotsKey {
2929

3030
impl FormalKey for ServerlessDesiredSlotsKey {
3131
/// Count.
32-
type Value = u32;
32+
type Value = i64;
3333

3434
fn deserialize(&self, raw: &[u8]) -> Result<Self::Value> {
3535
// NOTE: Atomic ops use little endian
36-
Ok(u32::from_le_bytes(raw.try_into()?))
36+
Ok(i64::from_le_bytes(raw.try_into()?))
3737
}
3838

3939
fn serialize(&self, value: Self::Value) -> Result<Vec<u8>> {

packages/common/util/core/src/math.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,30 @@ macro_rules! div_up {
66
($a + ($b - 1)) / $b
77
};
88
}
9+
10+
/// Performs ceiling division for i64 values.
11+
///
12+
/// Returns the smallest integer greater than or equal to `a / b`.
13+
///
14+
/// # Examples
15+
/// ```
16+
/// assert_eq!(div_ceil_i64(10, 3), 4); // 10/3 = 3.33.. -> 4
17+
/// assert_eq!(div_ceil_i64(9, 3), 3); // 9/3 = 3 -> 3
18+
/// assert_eq!(div_ceil_i64(-10, 3), -3); // -10/3 = -3.33.. -> -3
19+
/// ```
20+
///
21+
/// # Panics
22+
/// Panics if `b` is zero.
23+
pub fn div_ceil_i64(a: i64, b: i64) -> i64 {
24+
if b == 0 {
25+
panic!("attempt to divide by zero");
26+
}
27+
28+
if a == 0 || (a > 0 && b > 0) || (a < 0 && b < 0) {
29+
// Standard ceiling division when signs match or a is zero
30+
(a + b - 1) / b
31+
} else {
32+
// When signs differ, regular division gives the ceiling
33+
a / b
34+
}
35+
}

packages/core/pegboard-serverless/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ reqwest.workspace = true
1515
rivet-config.workspace = true
1616
rivet-runner-protocol.workspace = true
1717
rivet-types.workspace = true
18+
rivet-util.workspace = true
1819
tracing.workspace = true
1920
universaldb.workspace = true
2021
vbare.workspace = true

packages/core/pegboard-serverless/src/lib.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,25 @@ async fn tick(
142142
// Remove finished and draining connections from list
143143
curr.retain(|conn| !conn.handle.is_finished() && !conn.draining.load(Ordering::SeqCst));
144144

145-
let desired_count = (desired_slots.div_ceil(*slots_per_runner).max(*min_runners)
146-
+ *runners_margin)
147-
.min(*max_runners)
148-
.try_into()?;
145+
// Log warning and reset to 0 if negative
146+
let adjusted_desired_slots = if *desired_slots < 0 {
147+
tracing::warn!(
148+
?ns_id,
149+
?runner_name,
150+
desired_slots = ?desired_slots,
151+
"Negative desired_slots detected, resetting to 0"
152+
);
153+
0
154+
} else {
155+
*desired_slots
156+
};
157+
158+
let desired_count =
159+
(rivet_util::math::div_ceil_i64(adjusted_desired_slots, *slots_per_runner as i64)
160+
.max(*min_runners as i64)
161+
+ *runners_margin as i64)
162+
.min(*max_runners as i64)
163+
.try_into()?;
149164

150165
// Calculate diff
151166
let drain_count = curr.len().saturating_sub(desired_count);

packages/services/namespace/src/ops/runner_config/upsert.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ pub async fn namespace_runner_config_upsert(ctx: &OperationCtx, input: &Input) -
9595
input.namespace_id,
9696
input.name.clone(),
9797
),
98-
&0u32.to_le_bytes(),
98+
&0i64.to_le_bytes(),
9999
MutationType::Add,
100100
);
101101
}

packages/services/pegboard/src/workflows/actor/destroy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ pub(crate) async fn clear_slot(
240240
namespace_id,
241241
runner_name_selector.to_string(),
242242
),
243-
&(-1i32).to_le_bytes(),
243+
&(-1i64).to_le_bytes(),
244244
MutationType::Add,
245245
);
246246
}

packages/services/pegboard/src/workflows/actor/runtime.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ async fn allocate_actor(
129129
namespace_id,
130130
input.runner_name_selector.clone(),
131131
),
132-
&1u32.to_le_bytes(),
132+
&1i64.to_le_bytes(),
133133
MutationType::Add,
134134
);
135135
}
@@ -367,7 +367,7 @@ pub async fn deallocate(ctx: &ActivityCtx, input: &DeallocateInput) -> Result<()
367367
namespace_id,
368368
runner_name_selector.clone(),
369369
),
370-
&(-1i32).to_le_bytes(),
370+
&(-1i64).to_le_bytes(),
371371
MutationType::Add,
372372
);
373373
}

0 commit comments

Comments
 (0)