Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh

Commit

A workspace runs up to 12 jobs at once on g1t's machines while the pool has room, not 4. Every workspace was held to 4 hosted jobs at once (RUNNING_PER_WORKSPACE), so flagon-io's own CI and Deploy, about 13 jobs between them, waited their turn: 108 to 518 seconds after their runs were made, for 29aba25d, 58def404 and 0a5d1f6d, though the pool of 40 sandboxes (20 standard, 10 g1t-2core, 10 g1t-4core) was mostly idle. A workspace still always has its 4; past them, start_queued starts more, up to 12 (BURST_PER_WORKSPACE), while every workspace's hosted jobs together leave 8 sandboxes free (POOL_HEADROOM), so another workspace's first jobs still start at once (has_room). Deploy's own concurrency group still lands one deploy at a time. The architecture's Compute limits say so. Checked: cargo test -p g1t-actions-service (115 passing), npm run test:deploy (92).

G1t-Session:
ses_01m4y12gdde2r9q832m7xk87kd
syntaqxcommitted Parentfa02bf4Browse files
3 files+52−40/3 viewed
+1−1
5252
5353 | | |
5454 | --- | --- |
55−| Hosted jobs running at once | 4 per workspace |
55+| Hosted jobs running at once | 4 per workspace always, up to 12 while the pool of 40 sandboxes keeps 8 free for other workspaces |
5656 | A hosted job's timeout | 60 minutes at most |
5757 | A self-hosted job's timeout | 24 hours at most |
5858 | Logs | 4 MiB a job, kept 90 days |
+37−1
5959
6060 /// The most workflow files read from a repository.
6161 pub const MAX_WORKFLOWS: usize = 50;
62−/// Jobs one workspace may have running at once; the rest wait their turn.
62+/// Jobs on g1t's own machines one workspace may always have running at
63+/// once; the rest wait their turn, unless the pool has room (`has_room`).
6364 pub const RUNNING_PER_WORKSPACE: u32 = 4;
65+/// The most one workspace may run at once while the pool has room to spare.
66+pub const BURST_PER_WORKSPACE: u32 = 12;
67+/// Sandboxes g1t's own machines have between them (services/runner's
68+/// wrangler.jsonc: 20 standard, 10 g1t-2core, 10 g1t-4core).
69+pub const HOSTED_POOL: u32 = 40;
70+/// Kept free while workspaces run past their share, so another workspace's
71+/// first jobs still start at once.
72+pub const POOL_HEADROOM: u32 = 8;
73+
74+/// Whether a workspace running `in_workspace` jobs on g1t's machines may
75+/// start another while `in_pool` run across every workspace: always within
76+/// its share, and past it, up to the burst, while the pool keeps its
77+/// headroom.
78+pub fn has_room(in_workspace: u32, in_pool: u32) -> bool {
79+ in_workspace < RUNNING_PER_WORKSPACE
80+ || (in_workspace < BURST_PER_WORKSPACE && in_pool + POOL_HEADROOM < HOSTED_POOL)
81+}
6482 /// The longest a job on g1t's own machines may run, whatever its
6583 /// `timeout-minutes`.
6684 pub const MAX_TIMEOUT_MINUTES: u32 = g1t_actions::workflow::HOSTED_MAX_TIMEOUT_MINUTES;
404422 Err(error) => worker::console_error!("actions: could not start: {error}"),
405423 }
406424 }
425+
426+#[cfg(test)]
427+mod room_tests {
428+ use super::*;
429+
430+ #[test]
431+ fn a_workspace_always_has_its_share() {
432+ assert!(has_room(RUNNING_PER_WORKSPACE - 1, HOSTED_POOL));
433+ assert!(!has_room(RUNNING_PER_WORKSPACE, HOSTED_POOL));
434+ }
435+
436+ #[test]
437+ fn past_its_share_a_workspace_runs_more_while_the_pool_keeps_its_headroom() {
438+ assert!(has_room(RUNNING_PER_WORKSPACE, HOSTED_POOL - POOL_HEADROOM - 1));
439+ assert!(!has_room(RUNNING_PER_WORKSPACE, HOSTED_POOL - POOL_HEADROOM));
440+ assert!(!has_room(BURST_PER_WORKSPACE, 0));
441+ }
442+}
+14−2
2222 use crate::broker::{self, Broker};
2323 use crate::protection::Gate;
2424 use crate::sync::WorkflowRow;
25−use crate::{Actions, Count, MAX_TIMEOUT_MINUTES, RUNNING_PER_WORKSPACE, SELF_HOSTED_MAX_TIMEOUT_MINUTES, SILENT_MS, SITE, check, fail, optional, repo_path};
25+use crate::{Actions, Count, MAX_TIMEOUT_MINUTES, SELF_HOSTED_MAX_TIMEOUT_MINUTES, SILENT_MS, SITE, check, fail, has_room, optional, repo_path};
2626 use g1t_contracts::runners::{Wanted, waiting_reason};
2727
2828 /// The most log one job keeps, in bytes; past it, the log says so and stops.
15951595 .await?
15961596 .results::<JobRow>()?;
15971597 let mut running: std::collections::HashMap<String, u32> = std::collections::HashMap::new();
1598+ // Every workspace's jobs on g1t's own machines: past its share, a
1599+ // workspace starts more only while the pool has room (has_room).
1600+ let mut in_pool = if queued.is_empty() {
1601+ 0
1602+ } else {
1603+ self.db
1604+ .prepare("SELECT COUNT(*) AS n FROM jobs WHERE status = 'in_progress' AND runner_id IS NULL")
1605+ .first::<Count>(None)
1606+ .await?
1607+ .map_or(0, |count| count.n)
1608+ };
15981609 for job in queued {
15991610 let in_workspace = match running.get(&job.namespace) {
16001611 Some(n) => *n,
16111622 n
16121623 }
16131624 };
1614− if in_workspace >= RUNNING_PER_WORKSPACE {
1625+ if !has_room(in_workspace, in_pool) {
16151626 continue;
16161627 }
16171628 if let Some(max) = job.max_parallel {
16531664 continue;
16541665 }
16551666 running.insert(job.namespace.clone(), in_workspace + 1);
1667+ in_pool += 1;
16561668 self.db
16571669 .prepare("UPDATE runs SET status = 'in_progress', started_at = COALESCE(started_at, ?) WHERE id = ? AND status = 'queued'")
16581670 .bind(&[at.as_str().into(), job.run_id.as_str().into()])?

Comments

Sign in to comment.