Skip to main content

termivar_scanner/distributed/
limits.rs

1use super::DistributedError;
2
3/// Maximum task, scan, or worker identifier length in bytes.
4pub const MAX_IDENTIFIER_BYTES: usize = 256;
5/// Maximum opaque target reference length in bytes.
6pub const MAX_TARGET_REF_BYTES: usize = 1_024;
7/// Maximum phases carried by one task.
8pub const MAX_TASK_PHASES: usize = 256;
9/// Maximum observational worker metadata tags.
10pub const MAX_WORKER_TAGS: usize = 5;
11/// Integer utilization scale: 10,000 is 100%.
12pub const UTILIZATION_BASIS_POINTS: u16 = 10_000;
13/// Absolute ceiling for retained task records and terminal reservations.
14pub const MAX_TASK_RECORDS: usize = 65_536;
15/// Absolute ceiling for active and queued tasks.
16pub const MAX_ACTIVE_TASKS: usize = 16_384;
17/// Absolute ceiling for retained worker records.
18pub const MAX_WORKERS: usize = 4_096;
19/// Absolute ceiling for configured retries after the first attempt.
20pub const MAX_RETRIES: u32 = 32;
21/// Absolute ceiling for one worker's configured concurrency.
22pub const MAX_WORKER_CAPACITY: u32 = 4_096;
23/// Absolute ceiling for a lease TTL.
24pub const MAX_LEASE_TTL_SECS: u64 = 86_400;
25/// Absolute ceiling for task TTL policy.
26pub const MAX_TASK_TTL_SECS: u64 = 31 * 86_400;
27/// Absolute ceiling for heartbeat timeout policy.
28pub const MAX_HEARTBEAT_TIMEOUT_SECS: u64 = 86_400;
29/// Absolute ceiling for retained result records.
30pub const MAX_RESULTS: usize = 65_536;
31/// Absolute ceiling for one result.
32pub const MAX_RESULT_BYTES: usize = 16 * 1024 * 1024;
33/// Absolute ceiling for retained or aggregated result bytes.
34pub const MAX_TOTAL_RESULT_BYTES: usize = 256 * 1024 * 1024;
35/// Absolute ceiling for one aggregate request.
36pub const MAX_AGGREGATE_ITEMS: usize = 65_536;
37
38/// Hard coordinator bounds and fixed recovery policy.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub struct DistributedLimits {
41    pub max_task_records: usize,
42    pub max_active_tasks: usize,
43    pub max_queued_tasks: usize,
44    pub max_terminal_tasks: usize,
45    pub max_workers: usize,
46    pub max_retries: u32,
47    pub max_lease_ttl_secs: u64,
48    pub max_task_ttl_secs: u64,
49    pub heartbeat_timeout_secs: u64,
50}
51
52impl Default for DistributedLimits {
53    fn default() -> Self {
54        Self {
55            max_task_records: 4_096,
56            max_active_tasks: 1_024,
57            max_queued_tasks: 1_024,
58            max_terminal_tasks: 4_096,
59            max_workers: 256,
60            max_retries: 3,
61            max_lease_ttl_secs: 3_600,
62            max_task_ttl_secs: 86_400,
63            heartbeat_timeout_secs: 60,
64        }
65    }
66}
67
68pub(super) fn validate_limits(limits: DistributedLimits) -> Result<(), DistributedError> {
69    for (name, value) in [
70        ("max_task_records", limits.max_task_records),
71        ("max_active_tasks", limits.max_active_tasks),
72        ("max_queued_tasks", limits.max_queued_tasks),
73        ("max_terminal_tasks", limits.max_terminal_tasks),
74        ("max_workers", limits.max_workers),
75    ] {
76        if value == 0 {
77            return Err(DistributedError::InvalidLimit { name });
78        }
79    }
80    for (name, actual, maximum) in [
81        (
82            "max_task_records",
83            limits.max_task_records,
84            MAX_TASK_RECORDS,
85        ),
86        (
87            "max_active_tasks",
88            limits.max_active_tasks,
89            MAX_ACTIVE_TASKS,
90        ),
91        (
92            "max_queued_tasks",
93            limits.max_queued_tasks,
94            MAX_ACTIVE_TASKS,
95        ),
96        (
97            "max_terminal_tasks",
98            limits.max_terminal_tasks,
99            MAX_TASK_RECORDS,
100        ),
101        ("max_workers", limits.max_workers, MAX_WORKERS),
102    ] {
103        if actual > maximum {
104            return Err(DistributedError::CountLimitExceedsMaximum {
105                name,
106                actual,
107                maximum,
108            });
109        }
110    }
111    for (name, actual, maximum) in [
112        (
113            "max_lease_ttl_secs",
114            limits.max_lease_ttl_secs,
115            MAX_LEASE_TTL_SECS,
116        ),
117        (
118            "max_task_ttl_secs",
119            limits.max_task_ttl_secs,
120            MAX_TASK_TTL_SECS,
121        ),
122        (
123            "heartbeat_timeout_secs",
124            limits.heartbeat_timeout_secs,
125            MAX_HEARTBEAT_TIMEOUT_SECS,
126        ),
127    ] {
128        if actual > maximum {
129            return Err(DistributedError::TimeLimitExceedsMaximum {
130                name,
131                actual,
132                maximum,
133            });
134        }
135    }
136    if limits.max_retries > MAX_RETRIES {
137        return Err(DistributedError::RetryLimitExceedsMaximum {
138            actual: limits.max_retries,
139            maximum: MAX_RETRIES,
140        });
141    }
142    for (name, value) in [
143        ("max_lease_ttl_secs", limits.max_lease_ttl_secs),
144        ("max_task_ttl_secs", limits.max_task_ttl_secs),
145        ("heartbeat_timeout_secs", limits.heartbeat_timeout_secs),
146    ] {
147        if value == 0 {
148            return Err(DistributedError::InvalidLimit { name });
149        }
150    }
151    if limits.max_active_tasks > limits.max_task_records {
152        return Err(DistributedError::InvalidLimitRelationship {
153            reason: "max_active_tasks exceeds max_task_records",
154        });
155    }
156    if limits.max_queued_tasks > limits.max_active_tasks {
157        return Err(DistributedError::InvalidLimitRelationship {
158            reason: "max_queued_tasks exceeds max_active_tasks",
159        });
160    }
161    if limits.max_terminal_tasks > limits.max_task_records {
162        return Err(DistributedError::InvalidLimitRelationship {
163            reason: "max_terminal_tasks exceeds max_task_records",
164        });
165    }
166    if limits.max_terminal_tasks < limits.max_active_tasks {
167        return Err(DistributedError::InvalidLimitRelationship {
168            reason: "max_terminal_tasks is smaller than max_active_tasks",
169        });
170    }
171    Ok(())
172}