Repository navigation
Expand file tree
/
Copy pathcampaign-lib.sh
More file actions
95 lines (89 loc) · 4.66 KB
/
Copy pathcampaign-lib.sh
File metadata and controls
95 lines (89 loc) · 4.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# Shared helpers for run-campaign.sh and run-paired-campaign.sh.
#
# Sourced, not executed. Everything here is about two things a campaign must get right:
# the order units run in, and a durable record of which units are finished.
# -------------------------------------------------------------------------------------
# Deterministic shuffling
# -------------------------------------------------------------------------------------
# Randomising run order is the whole point of shuffling here (see
# discussion/hop-latency-statistical-analysis.md section 8.2): with cells run in increasing
# N, anything that drifts over a session - network conditions, a warming machine, an
# accumulating browser leak - is confounded with the variable under study.
#
# But the order must be reproducible, or a campaign cannot be resumed identically after an
# interruption and cannot be reported in a paper. So: a seeded Park-Miller generator written
# out longhand rather than awk's rand(), whose seeding is implementation-defined, or
# shuf --random-source, whose mapping from bytes to permutations is not specified. 16807 *
# 2147483646 is under 2^53, so the multiply is exact in awk's doubles.
shuffle_lines() {
local seed="$1"
awk -v seed="$seed" '
function next_rand( ) { s = (16807 * s) % 2147483647; return s / 2147483647 }
BEGIN {
s = seed % 2147483646
if (s <= 0) s += 2147483646
# Discard the first few outputs. Park-Miller advances by a factor of 16807 per
# step, so adjacent seeds produce near-identical first outputs - seeds n and n+1
# differ by 16807 out of 2^31, which is not enough to change a two-way shuffle at
# all. Five steps multiply that gap by 16807^5 and put the streams anywhere.
for (w = 0; w < 5; w++) s = (16807 * s) % 2147483647
}
{ line[NR] = $0 }
END {
for (i = NR; i > 1; i--) {
j = 1 + int(next_rand() * i)
if (j > i) j = i
tmp = line[i]; line[i] = line[j]; line[j] = tmp
}
for (i = 1; i <= NR; i++) print line[i]
}
'
}
# -------------------------------------------------------------------------------------
# Manifest
# -------------------------------------------------------------------------------------
# A campaign of a few hundred units spanning many hours will be interrupted. The manifest is
# what makes a re-run an append rather than a restart, and what records the conditions each
# unit ran under - order index and seed included, so run order can be used as a covariate in
# the analysis instead of merely being argued about.
MANIFEST_HEADER="resolution,workers,try,tries,duration_s,pipe_addressing,order_index,seed,status,started_utc,finished_utc,seconds"
manifest_init() {
local manifest="$1"
mkdir -p "$(dirname "$manifest")"
if [ ! -f "$manifest" ]; then
echo "$MANIFEST_HEADER" > "$manifest"
return
fi
# A v1 manifest (no try/pipe_addressing columns) belongs to the thesis-era grid. Reading
# it with v2 column positions would silently mark the wrong units done, so refuse.
local header
header=$(head -1 "$manifest")
if [ "$header" != "$MANIFEST_HEADER" ]; then
echo "Error: $manifest has an older column layout:"
echo " found: $header"
echo " expected: $MANIFEST_HEADER"
echo "Point MANIFEST/RESULTS_ROOT at a fresh campaign root, or archive the old one."
return 1
fi
}
# manifest_done <manifest> <resolution> <workers> <try> ("all" for a whole-cell unit)
manifest_done() {
awk -F, -v r="$2" -v w="$3" -v t="$4" \
'$1==r && $2==w && $3==t && $9=="done" { found=1 } END { exit !found }' "$1"
}
# manifest_append <manifest> <res> <workers> <try> <tries> <duration> <addressing> <order> <seed> <status> <started> <finished> <seconds>
manifest_append() {
local manifest="$1"; shift
(IFS=,; echo "$*") >> "$manifest"
}
# -------------------------------------------------------------------------------------
# Credentials
# -------------------------------------------------------------------------------------
# A campaign outlives an MFA session. When the token expires mid-unit every AWS call starts
# failing - including the terminate-instances in launch-experiment.sh's cleanup - so the unit
# is marked failed and the loop moves on, stranding that unit's workers. That is exactly how
# a 2026-08-21 run left 101 instances billing. Check before each unit and abort the whole
# campaign instead: units not yet attempted stay out of the manifest and resume cleanly.
preflight_credentials() {
aws sts get-caller-identity --region "${REGION:-eu-west-1}" >/dev/null 2>&1
}