Skip to content

Commit 4d36ee2

Browse files
authored
Fix disk budget for bounded workspaces (#630)
* Fix disk budget for bounded workspaces * Satisfy disk budget threshold types
1 parent e231b94 commit 4d36ee2

2 files changed

Lines changed: 62 additions & 7 deletions

File tree

inc/Workspace/WorktreeDiskBudget.php

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,16 @@ public static function evaluate( array $metrics, array $thresholds = array(), bo
9393
$effective_refuse_bytes = $thresholds['refuse_free_bytes'];
9494
$effective_warn_bytes = $thresholds['warn_free_bytes'];
9595
if ( null !== $total_bytes && $total_bytes > 0 ) {
96-
$effective_refuse_bytes = max($effective_refuse_bytes, (int) ceil($total_bytes * ( $thresholds['refuse_free_percent'] / 100 )));
97-
$effective_warn_bytes = max($effective_warn_bytes, (int) ceil($total_bytes * ( $thresholds['warn_free_percent'] / 100 )));
96+
$effective_refuse_bytes = self::effective_free_bytes_threshold(
97+
(int) $thresholds['refuse_free_bytes'],
98+
$thresholds['refuse_free_percent'],
99+
$total_bytes
100+
);
101+
$effective_warn_bytes = self::effective_free_bytes_threshold(
102+
(int) $thresholds['warn_free_bytes'],
103+
$thresholds['warn_free_percent'],
104+
$total_bytes
105+
);
98106
}
99107

100108
if ( null !== $free_bytes ) {
@@ -104,15 +112,15 @@ public static function evaluate( array $metrics, array $thresholds = array(), bo
104112
'Free disk space is %.1f GiB%s, below the refusal threshold of %.1f GiB or %.1f%% free, whichever is stricter.',
105113
self::bytes_to_gib($free_bytes),
106114
null === $free_percent ? '' : sprintf(' (%.1f%%)', $free_percent),
107-
self::bytes_to_gib($thresholds['refuse_free_bytes']),
115+
self::bytes_to_gib( (int) $thresholds['refuse_free_bytes'] ),
108116
$thresholds['refuse_free_percent']
109117
);
110118
} elseif ( $free_bytes < $effective_warn_bytes ) {
111119
$warnings[] = sprintf(
112120
'Free disk space is %.1f GiB%s, below the warning threshold of %.1f GiB or %.1f%% free, whichever is stricter.',
113121
self::bytes_to_gib($free_bytes),
114122
null === $free_percent ? '' : sprintf(' (%.1f%%)', $free_percent),
115-
self::bytes_to_gib($thresholds['warn_free_bytes']),
123+
self::bytes_to_gib( (int) $thresholds['warn_free_bytes'] ),
116124
$thresholds['warn_free_percent']
117125
);
118126
}
@@ -150,10 +158,10 @@ public static function evaluate( array $metrics, array $thresholds = array(), bo
150158
'workspace_size_exact' => false,
151159
'worktree_count' => $count,
152160
'warn_free_bytes' => $thresholds['warn_free_bytes'],
153-
'warn_free_gib' => round(self::bytes_to_gib($thresholds['warn_free_bytes']), 2),
161+
'warn_free_gib' => round(self::bytes_to_gib( (int) $thresholds['warn_free_bytes'] ), 2),
154162
'warn_free_percent' => $thresholds['warn_free_percent'],
155163
'refuse_free_bytes' => $thresholds['refuse_free_bytes'],
156-
'refuse_free_gib' => round(self::bytes_to_gib($thresholds['refuse_free_bytes']), 2),
164+
'refuse_free_gib' => round(self::bytes_to_gib( (int) $thresholds['refuse_free_bytes'] ), 2),
157165
'refuse_free_percent' => $thresholds['refuse_free_percent'],
158166
'effective_refuse_bytes' => $effective_refuse_bytes,
159167
'effective_refuse_gib' => round(self::bytes_to_gib($effective_refuse_bytes), 2),
@@ -295,6 +303,28 @@ private static function count_worktree_like_dirs( string $workspace_path ): int
295303
return $count;
296304
}
297305

306+
/**
307+
* Calculate the free-space threshold for the measured filesystem.
308+
*
309+
* The absolute GiB floor protects normal workspaces, but bounded ephemeral
310+
* filesystems can be smaller than that floor. In that case, the percentage
311+
* threshold is the only attainable safety signal.
312+
*
313+
* @param int $absolute_bytes Absolute free-space threshold.
314+
* @param float $percent Percentage free-space threshold.
315+
* @param int $total_bytes Measured filesystem size.
316+
* @return int
317+
*/
318+
private static function effective_free_bytes_threshold( int $absolute_bytes, float $percent, int $total_bytes ): int {
319+
$percent_bytes = (int) ceil($total_bytes * ( $percent / 100 ));
320+
321+
if ( $total_bytes < $absolute_bytes ) {
322+
return $percent_bytes;
323+
}
324+
325+
return max($absolute_bytes, $percent_bytes);
326+
}
327+
298328
/**
299329
* Convert bytes to GiB.
300330
*

tests/smoke-worktree-disk-budget.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,32 @@ function datamachine_code_budget_assert( bool $condition, string $message ): voi
127127
datamachine_code_budget_assert(true === $budget['emergency_triggered'], 'worktree count warning triggers emergency cleanup');
128128
datamachine_code_budget_assert(str_contains($budget['warnings'][0], '101 worktree-like directories'), 'worktree-count warning is descriptive');
129129

130-
echo "\n[7] inspect counts only worktree-like directories\n";
130+
echo "\n[7] small ephemeral filesystems use percentage thresholds\n";
131+
$budget = WorktreeDiskBudget::evaluate(
132+
array(
133+
'workspace_path' => '/workspace',
134+
'free_bytes' => 2 * $gib,
135+
'total_bytes' => 4 * $gib,
136+
'worktree_count' => 0,
137+
),
138+
$thresholds
139+
);
140+
datamachine_code_budget_assert('ok' === $budget['status'], 'small workspace with 50 percent free is ok');
141+
datamachine_code_budget_assert(0.4 === $budget['effective_refuse_gib'], 'small workspace refusal floor uses percentage threshold');
142+
datamachine_code_budget_assert(0.6 === $budget['effective_warn_gib'], 'small workspace warning floor uses percentage threshold');
143+
144+
$budget = WorktreeDiskBudget::evaluate(
145+
array(
146+
'workspace_path' => '/workspace',
147+
'free_bytes' => (int) ( 0.2 * $gib ),
148+
'total_bytes' => 4 * $gib,
149+
'worktree_count' => 0,
150+
),
151+
$thresholds
152+
);
153+
datamachine_code_budget_assert('refused' === $budget['status'], 'small workspace still refuses below percentage threshold');
154+
155+
echo "\n[8] inspect counts only worktree-like directories\n";
131156
$tmp = sys_get_temp_dir() . '/dmc-budget-' . uniqid('', true);
132157
mkdir($tmp);
133158
mkdir($tmp . '/repo');

0 commit comments

Comments
 (0)