Error budget
A 99.9% monthly target: two months that spend the same budget reach 42 users and 1,000, an hour with no requests reads as fully met, and one endpoint spends 82% of a budget the service never exceeds.
Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.
About
A service with a 99.9% monthly target, a budget of 43.2 minutes of outage a month, and what the budget does not count.
main.eml- the menu, the service, two months, a day with an empty hour, one quarter's budget over six endpoints, and outages of your ownbudget.eml- the budget, the users and their hours, the outages of each month, the day's hourly requests, and the endpoints
How each part works:
- The 1,000 users are each active one hour a day, the same hour every day, spread evenly over the 24 hours. An outage interrupts the users of its hour.
- As in the corpus case
the-error-budget-was-monthly-and-the-outage-was-not, month A has one outage of 42.0 minutes and month B thirty of 1.4, a day apart at different hours: the budget reads 1.2 minutes left in both. Month A interrupted the 42 users of one hour; month B all 1,000, and 251 of them twice. - As in
the-slo-was-met-over-a-window-with-no-requests, a latency objective met hour by hour reads 100.00% for 03:00, when the load balancer sent the service nothing: 0 misses of 0 requests. Counted as no answer, with a check that fires on an empty hour, it pages. - As in
the-error-budget-was-spent-evenly-and-one-endpoint-ate-it, one budget of 1.00% over all six endpoints reads 0.78%, inside it. /export fails 43.44% of its requests on 1.48% of the traffic and spends 82.18% of the budget used; with a budget per endpoint it is the one frozen.
What is checked: menu choices 1 to 6; 1 to 60 outages; minutes each, as a whole number or to one decimal place, from 0.1 to 43.2. An empty answer cancels.
Sessions: sessions/basic.in gives: - the service, the two months, the day's hours and the endpoints; - your own: 30 outages of 1.4 minutes, 1 of 42, and 60 of 1 (over the budget).
sessions/bad-input.in gives: - menu choices 0 and x; - outages: a cancel, 0, 61 and x; minutes: a cancel, 0, 0.0, 43.3, 1.45, .5 and 1,5; - 24 outages of 0.1 minutes, and 2 of 43.2.
Built on the verified corpus cases the-error-budget-was-monthly-and-the-outage-was-not (two months that spend the same budget), the-slo-was-met-over-a-window-with-no-requests (an hour with no requests read as fully met) and the-error-budget-was-spent-evenly-and-one-endpoint-ate-it (one quarterly budget over 214 endpoints).
Recorded sessions
What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.
bad-input
interpreter: byte-equal== Error budget ==
A monthly budget of outage minutes, and what it does not count.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 0
Pick a number from 1 to 6.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> x
Pick a number from 1 to 6.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)>
Cancelled.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 0
Type a whole number from 1 to 60.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 61
Type a whole number from 1 to 60.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> x
Type a whole number from 1 to 60.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 24
minutes each, to a tenth (0.1 to 43.2)>
Cancelled.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 24
minutes each, to a tenth (0.1 to 43.2)> 0
Type minutes such as 1.4, from 0.1 to 43.2.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 24
minutes each, to a tenth (0.1 to 43.2)> 0.0
Type minutes such as 1.4, from 0.1 to 43.2.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 24
minutes each, to a tenth (0.1 to 43.2)> 43.3
Type minutes such as 1.4, from 0.1 to 43.2.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 24
minutes each, to a tenth (0.1 to 43.2)> 1.45
Type minutes such as 1.4, from 0.1 to 43.2.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 24
minutes each, to a tenth (0.1 to 43.2)> .5
Type minutes such as 1.4, from 0.1 to 43.2.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 24
minutes each, to a tenth (0.1 to 43.2)> 1,5
Type minutes such as 1.4, from 0.1 to 43.2.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 24
minutes each, to a tenth (0.1 to 43.2)> 0.1
24 outages of 0.1 minutes spend 2.4 of the 43.2 minutes, 40.8 left.
They interrupt 1,000 of the 1,000 users, 0 of them more than once.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 2
minutes each, to a tenth (0.1 to 43.2)> 43.2
2 outages of 43.2 minutes spend 86.4 minutes, 43.2 over the budget.
They interrupt 84 of the 1,000 users, 0 of them more than once.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 6
Bye.
What was typed (38 lines)
0
x
5
5
0
5
61
5
x
5
24
5
24
0
5
24
0.0
5
24
43.3
5
24
1.45
5
24
.5
5
24
1,5
5
24
0.1
5
2
43.2
6
basic
interpreter: byte-equal== Error budget ==
A monthly budget of outage minutes, and what it does not count.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 1
A service with a 99.9% monthly target: 0.1% of a 30-day month is a budget of 43.2 minutes of outage.
Its 1,000 users are each active one hour a day, the same hour every day, spread evenly over the 24 hours.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 2
Two months, each inside the budget:
month A: 1 outage, on day 10 at 14:10, 42.0 minutes
month B: 30 outages of 1.4 minutes, one a day, at 00:10, 07:10, 14:10, 21:10, 04:10 and so on
month outages minutes budget left users hit
A 1 42.0 1.2 42
B 30 42.0 1.2 1,000
The budget reads the same for both. Month A interrupted the 42 users of one hour; month B interrupted all 1,000, and 251 of them twice.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 3
A day's latency objective, hour by hour: a request over the threshold is a miss, and an hour meets the objective at 99.50% or better.
hour requests misses met
00:00 800 2 99.75%
01:00 1,550 3 99.81%
02:00 1,250 3 99.76%
03:00 0 0 100.00%
04:00 1,700 4 99.76%
05:00 1,400 3 99.79%
06:00 1,100 2 99.82%
07:00 800 2 99.75%
08:00 1,550 3 99.81%
09:00 1,250 3 99.76%
10:00 950 2 99.79%
11:00 1,700 4 99.76%
12:00 1,400 3 99.79%
13:00 1,100 2 99.82%
14:00 800 2 99.75%
15:00 1,550 3 99.81%
16:00 1,250 3 99.76%
17:00 950 2 99.79%
18:00 1,700 4 99.76%
19:00 1,400 3 99.79%
20:00 1,100 2 99.82%
21:00 800 2 99.75%
22:00 1,550 3 99.81%
23:00 1,250 3 99.76%
24 of 24 hours meet it. 03:00 reads 100.00% from 0 requests: the load balancer sent nothing to the service that hour, and nobody could reach it.
With an hour of no requests counted as no answer, and an availability check that fires on an empty hour, 03:00 pages instead.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 4
One budget for the quarter over every endpoint: at most 1.00% of requests may fail.
endpoint requests errors error rate of the errors
/search 24,000,000 31,000 0.13% 6.52%
/orders 18,000,000 26,000 0.14% 5.46%
/cart 11,000,000 14,000 0.13% 2.94%
/login 6,100,000 13,800 0.23% 2.90%
/export 900,000 391,000 43.44% 82.18%
/health 1,000,000 0 0.00% 0.00%
All endpoints: 61,000,000 requests, 475,800 errors, 0.78%: inside the budget, and no freeze.
/export fails 43.44% of its requests on 1.48% of the traffic, and spends 82.18% of the budget used.
With a budget per endpoint, in proportion to its traffic, /export is the one endpoint over its own and the one frozen.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 30
minutes each, to a tenth (0.1 to 43.2)> 1.4
30 outages of 1.4 minutes spend 42.0 of the 43.2 minutes, 1.2 left.
They interrupt 1,000 of the 1,000 users, 251 of them more than once.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 1
minutes each, to a tenth (0.1 to 43.2)> 42
1 outage of 42.0 minutes spends 42.0 of the 43.2 minutes, 1.2 left.
It interrupts 42 of the 1,000 users, 0 of them more than once.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 5
outages (1 to 60)> 60
minutes each, to a tenth (0.1 to 43.2)> 1
60 outages of 1.0 minutes spend 60.0 minutes, 16.8 over the budget.
They interrupt 1,000 of the 1,000 users, 1,000 of them more than once.
1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit
choice> 6
Bye.
What was typed (14 lines)
1
2
3
4
5
30
1.4
5
1
42
5
60
1
6
Modules
The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.
main.eml(entry)
eml# P172 error budget: a 99.9% monthly target - two months that spend the
# same budget, a day's latency objective with an empty hour, one quarter's
# budget over six endpoints, and outages of your own.
import budget
def plain(s):
0 => i
len(s) => j
while i < j and s[i] == " ":
i + 1 => i
while j > i and s[j - 1] == " ":
j - 1 => j
return s[i:j]
def pad(s, n):
s => out
while len(out) < n:
out + " " => out
return out
def lpad(s, n):
s => out
while len(out) < n:
" " + out => out
return out
def digits(t):
for c in t:
if c < "0" or c > "9":
return False
return len(t) > 0
def commas(n):
str(n) => s
"" => out
0 => k
len(s) => i
while i > 0:
if k == 3:
"," + out => out
0 => k
s[i - 1] + out => out
k + 1 => k
i - 1 => i
return out
def tenths(t):
return commas(int((t - t % 10) / 10)) + "." + str(t % 10)
def per(a, b):
# a of b as a percent to two places, half-up
a * 20000 + b => y
int((y - y % (2 * b)) / (2 * b)) => h
h % 100 => r
if r < 10:
return str(int((h - r) / 100)) + ".0" + str(r) + "%"
return str(int((h - r) / 100)) + "." + str(r) + "%"
def clock(h, m):
"" => s
if h < 10:
"0" => s
s + str(h) + ":" => s
if m < 10:
s + "0" => s
return s + str(m)
def twice(outs):
# users active in the hours of two or more outages
[0] * 24 => n
for o in outs:
n[o[1]] + 1 => n[o[1]]
0 => c
0 => u
while u < budget.users():
if n[u % 24] >= 2:
c + 1 => c
u + 1 => u
return c
def show_service():
("A service with a 99.9% monthly target: 0.1% of a 30-day month is a budget of " + tenths(budget.budget_tenths()) + " minutes of outage.") ^0
("Its " + commas(budget.users()) + " users are each active one hour a day, the same hour every day, spread evenly over the 24 hours.") ^0
def month_row(name, outs):
budget.spent(outs) => s
(" " + pad(name, 7) + lpad(str(len(outs)), 7) + lpad(tenths(s), 9) + lpad(tenths(budget.budget_tenths() - s), 13) + lpad(commas(budget.hit(outs)), 11)) ^0
def show_months():
budget.month_a() => a
budget.spread(30, 14) => b
"Two months, each inside the budget:" ^0
(" month A: 1 outage, on day " + str(a[0][0]) + " at " + clock(a[0][1], a[0][2]) + ", " + tenths(a[0][3]) + " minutes") ^0
" month B: 30 outages of 1.4 minutes, one a day, at 00:10, 07:10, 14:10, 21:10, 04:10 and so on" ^0
(" " + pad("month", 7) + lpad("outages", 7) + lpad("minutes", 9) + lpad("budget left", 13) + lpad("users hit", 11)) ^0
month_row("A", a)
month_row("B", b)
("The budget reads the same for both. Month A interrupted the " + str(budget.hit(a)) + " users of one hour; month B interrupted all " + commas(budget.hit(b)) + ", and " + str(twice(b)) + " of them twice.") ^0
def show_hours():
budget.hourly() => hs
"A day's latency objective, hour by hour: a request over the threshold is a miss, and an hour meets the objective at 99.50% or better." ^0
(" " + pad("hour", 7) + lpad("requests", 9) + lpad("misses", 8) + lpad("met", 10)) ^0
0 => met
for h in hs:
"100.00%" => shown
if h[1] > 0:
per(h[1] - h[2], h[1]) => shown
if h[1] == 0 or (h[1] - h[2]) * 10000 >= 9950 * h[1]:
met + 1 => met
(" " + pad(clock(h[0], 0), 7) + lpad(commas(h[1]), 9) + lpad(str(h[2]), 8) + lpad(shown, 10)) ^0
(str(met) + " of 24 hours meet it. 03:00 reads 100.00% from 0 requests: the load balancer sent nothing to the service that hour, and nobody could reach it.") ^0
"With an hour of no requests counted as no answer, and an availability check that fires on an empty hour, 03:00 pages instead." ^0
def show_endpoints():
budget.endpoints() => es
0 => req
0 => err
for e in es:
req + e[1] => req
err + e[2] => err
("One budget for the quarter over every endpoint: at most " + per(budget.budget_rate(), 10000) + " of requests may fail.") ^0
(" " + pad("endpoint", 10) + lpad("requests", 12) + lpad("errors", 9) + lpad("error rate", 12) + lpad("of the errors", 15)) ^0
[] => over
for e in es:
(" " + pad(e[0], 10) + lpad(commas(e[1]), 12) + lpad(commas(e[2]), 9) + lpad(per(e[2], e[1]), 12) + lpad(per(e[2], err), 15)) ^0
if e[2] * 10000 > budget.budget_rate() * e[1]:
over + [e[0]] => over
("All endpoints: " + commas(req) + " requests, " + commas(err) + " errors, " + per(err, req) + ": inside the budget, and no freeze.") ^0
es[4] => x
(x[0] + " fails " + per(x[2], x[1]) + " of its requests on " + per(x[1], req) + " of the traffic, and spends " + per(x[2], err) + " of the budget used.") ^0
if len(over) == 1:
("With a budget per endpoint, in proportion to its traffic, " + over[0] + " is the one endpoint over its own and the one frozen.") ^0
def own():
plain(input("outages (1 to 60)> ")) => t
if t == "":
"Cancelled." ^0
return
if len(t) > 2 or not digits(t) or int(t) < 1 or int(t) > 60:
"Type a whole number from 1 to 60." ^0
return
int(t) => n
plain(input("minutes each, to a tenth (0.1 to 43.2)> ")) => m
if m == "":
"Cancelled." ^0
return
0 - 1 => tt
if digits(m) and len(m) <= 2:
int(m) * 10 => tt
elif len(m) >= 3 and len(m) <= 4 and m[len(m) - 2] == "." and digits(m[0:len(m) - 2]) and digits(m[len(m) - 1:]):
int(m[0:len(m) - 2]) * 10 + int(m[len(m) - 1:]) => tt
if tt < 1 or tt > 432:
"Type minutes such as 1.4, from 0.1 to 43.2." ^0
return
budget.spread(n, tt) => outs
budget.spent(outs) => s
str(n) + " outages of " + tenths(tt) + " minutes spend " => lead
if n == 1:
"1 outage of " + tenths(tt) + " minutes spends " => lead
if s <= budget.budget_tenths():
(lead + tenths(s) + " of the " + tenths(budget.budget_tenths()) + " minutes, " + tenths(budget.budget_tenths() - s) + " left.") ^0
else:
(lead + tenths(s) + " minutes, " + tenths(s - budget.budget_tenths()) + " over the budget.") ^0
"They interrupt " => verb
if n == 1:
"It interrupts " => verb
(verb + commas(budget.hit(outs)) + " of the " + commas(budget.users()) + " users, " + commas(twice(outs)) + " of them more than once.") ^0
"== Error budget ==" ^0
"A monthly budget of outage minutes, and what it does not count." ^0
True => running
while running:
"" ^0
"1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit" ^0
plain(input("choice> ")) => choice
if choice == "1":
show_service()
elif choice == "2":
show_months()
elif choice == "3":
show_hours()
elif choice == "4":
show_endpoints()
elif choice == "5":
own()
elif choice == "6":
False => running
else:
"Pick a number from 1 to 6." ^0
"Bye." ^0
Python projection (main.py)
import budget
def plain(s):
i = 0
j = len(s)
while i < j and s[i] == " ":
i = i + 1
while j > i and s[j - 1] == " ":
j = j - 1
return s[i:j]
def pad(s, n):
out = s
while len(out) < n:
out = out + " "
return out
def lpad(s, n):
out = s
while len(out) < n:
out = " " + out
return out
def digits(t):
for c in t:
if c < "0" or c > "9":
return False
return len(t) > 0
def commas(n):
s = str(n)
out = ""
k = 0
i = len(s)
while i > 0:
if k == 3:
out = "," + out
k = 0
out = s[i - 1] + out
k = k + 1
i = i - 1
return out
def tenths(t):
return commas(int((t - t % 10) / 10)) + "." + str(t % 10)
def per(a, b):
y = a * 20000 + b
h = int((y - y % (2 * b)) / (2 * b))
r = h % 100
if r < 10:
return str(int((h - r) / 100)) + ".0" + str(r) + "%"
return str(int((h - r) / 100)) + "." + str(r) + "%"
def clock(h, m):
s = ""
if h < 10:
s = "0"
s = s + str(h) + ":"
if m < 10:
s = s + "0"
return s + str(m)
def twice(outs):
n = [0] * 24
for o in outs:
n[o[1]] = n[o[1]] + 1
c = 0
u = 0
while u < budget.users():
if n[u % 24] >= 2:
c = c + 1
u = u + 1
return c
def show_service():
print("A service with a 99.9% monthly target: 0.1% of a 30-day month is a budget of " + tenths(budget.budget_tenths()) + " minutes of outage.")
print("Its " + commas(budget.users()) + " users are each active one hour a day, the same hour every day, spread evenly over the 24 hours.")
def month_row(name, outs):
s = budget.spent(outs)
print(" " + pad(name, 7) + lpad(str(len(outs)), 7) + lpad(tenths(s), 9) + lpad(tenths(budget.budget_tenths() - s), 13) + lpad(commas(budget.hit(outs)), 11))
def show_months():
a = budget.month_a()
b = budget.spread(30, 14)
print("Two months, each inside the budget:")
print(" month A: 1 outage, on day " + str(a[0][0]) + " at " + clock(a[0][1], a[0][2]) + ", " + tenths(a[0][3]) + " minutes")
print(" month B: 30 outages of 1.4 minutes, one a day, at 00:10, 07:10, 14:10, 21:10, 04:10 and so on")
print(" " + pad("month", 7) + lpad("outages", 7) + lpad("minutes", 9) + lpad("budget left", 13) + lpad("users hit", 11))
month_row("A", a)
month_row("B", b)
print("The budget reads the same for both. Month A interrupted the " + str(budget.hit(a)) + " users of one hour; month B interrupted all " + commas(budget.hit(b)) + ", and " + str(twice(b)) + " of them twice.")
def show_hours():
hs = budget.hourly()
print("A day's latency objective, hour by hour: a request over the threshold is a miss, and an hour meets the objective at 99.50% or better.")
print(" " + pad("hour", 7) + lpad("requests", 9) + lpad("misses", 8) + lpad("met", 10))
met = 0
for h in hs:
shown = "100.00%"
if h[1] > 0:
shown = per(h[1] - h[2], h[1])
if h[1] == 0 or (h[1] - h[2]) * 10000 >= 9950 * h[1]:
met = met + 1
print(" " + pad(clock(h[0], 0), 7) + lpad(commas(h[1]), 9) + lpad(str(h[2]), 8) + lpad(shown, 10))
print(str(met) + " of 24 hours meet it. 03:00 reads 100.00% from 0 requests: the load balancer sent nothing to the service that hour, and nobody could reach it.")
print("With an hour of no requests counted as no answer, and an availability check that fires on an empty hour, 03:00 pages instead.")
def show_endpoints():
es = budget.endpoints()
req = 0
err = 0
for e in es:
req = req + e[1]
err = err + e[2]
print("One budget for the quarter over every endpoint: at most " + per(budget.budget_rate(), 10000) + " of requests may fail.")
print(" " + pad("endpoint", 10) + lpad("requests", 12) + lpad("errors", 9) + lpad("error rate", 12) + lpad("of the errors", 15))
over = []
for e in es:
print(" " + pad(e[0], 10) + lpad(commas(e[1]), 12) + lpad(commas(e[2]), 9) + lpad(per(e[2], e[1]), 12) + lpad(per(e[2], err), 15))
if e[2] * 10000 > budget.budget_rate() * e[1]:
over = over + [e[0]]
print("All endpoints: " + commas(req) + " requests, " + commas(err) + " errors, " + per(err, req) + ": inside the budget, and no freeze.")
x = es[4]
print(x[0] + " fails " + per(x[2], x[1]) + " of its requests on " + per(x[1], req) + " of the traffic, and spends " + per(x[2], err) + " of the budget used.")
if len(over) == 1:
print("With a budget per endpoint, in proportion to its traffic, " + over[0] + " is the one endpoint over its own and the one frozen.")
def own():
t = plain(input("outages (1 to 60)> "))
if t == "":
print("Cancelled.")
return
if len(t) > 2 or not digits(t) or int(t) < 1 or int(t) > 60:
print("Type a whole number from 1 to 60.")
return
n = int(t)
m = plain(input("minutes each, to a tenth (0.1 to 43.2)> "))
if m == "":
print("Cancelled.")
return
tt = 0 - 1
if digits(m) and len(m) <= 2:
tt = int(m) * 10
elif len(m) >= 3 and len(m) <= 4 and m[len(m) - 2] == "." and digits(m[0:len(m) - 2]) and digits(m[len(m) - 1:]):
tt = int(m[0:len(m) - 2]) * 10 + int(m[len(m) - 1:])
if tt < 1 or tt > 432:
print("Type minutes such as 1.4, from 0.1 to 43.2.")
return
outs = budget.spread(n, tt)
s = budget.spent(outs)
lead = str(n) + " outages of " + tenths(tt) + " minutes spend "
if n == 1:
lead = "1 outage of " + tenths(tt) + " minutes spends "
if s <= budget.budget_tenths():
print(lead + tenths(s) + " of the " + tenths(budget.budget_tenths()) + " minutes, " + tenths(budget.budget_tenths() - s) + " left.")
else:
print(lead + tenths(s) + " minutes, " + tenths(s - budget.budget_tenths()) + " over the budget.")
verb = "They interrupt "
if n == 1:
verb = "It interrupts "
print(verb + commas(budget.hit(outs)) + " of the " + commas(budget.users()) + " users, " + commas(twice(outs)) + " of them more than once.")
print("== Error budget ==")
print("A monthly budget of outage minutes, and what it does not count.")
running = True
while running:
print("")
print("1) the service 2) two months 3) an empty hour 4) one budget, six endpoints 5) outages of your own 6) quit")
choice = plain(input("choice> "))
if choice == "1":
show_service()
elif choice == "2":
show_months()
elif choice == "3":
show_hours()
elif choice == "4":
show_endpoints()
elif choice == "5":
own()
elif choice == "6":
running = False
else:
print("Pick a number from 1 to 6.")
print("Bye.")
budget.eml
eml# P172 error budget - a service with a 99.9% monthly target, 43.2 minutes
# of budget a month. As in the corpus case
# the-error-budget-was-monthly-and-the-outage-was-not, a budget sums the
# minutes and forgets when they fell and whom they reached. As in
# the-slo-was-met-over-a-window-with-no-requests, a window with no requests
# reads as fully met. And as in
# the-error-budget-was-spent-evenly-and-one-endpoint-ate-it, one budget
# over every endpoint can be almost all one endpoint's.
def budget_tenths():
# 0.1% of a 30-day month, in tenths of a minute
return 432
def users():
# each of the 1,000 users is active one hour a day, the hour u % 24
return 1000
def month_a():
# [day, hour, minute it starts, tenths of a minute] - one outage
return [[10, 14, 10, 420]]
def spread(n, tenths):
# n outages of the same length, one a day, at the hour 7 * i % 24
[] => out
0 => i
while i < n:
out + [[i % 30 + 1, (7 * i) % 24, 10, tenths]] => out
i + 1 => i
return out
def spent(outs):
0 => t
for o in outs:
t + o[3] => t
return t
def hit(outs):
# users active in the hour of at least one outage
[False] * 24 => hours
for o in outs:
True => hours[o[1]]
0 => n
0 => u
while u < users():
if hours[u % 24]:
n + 1 => n
u + 1 => u
return n
def hourly():
# [hour, requests, requests over the latency threshold] for one day;
# from 03:00 to 04:00 the load balancer sent nothing to the service
[] => out
0 => h
while h < 24:
800 + 150 * ((h * 5) % 7) => r
if h == 3:
0 => r
out + [[h, r, int(r / 400)]] => out
h + 1 => h
return out
def endpoints():
# [endpoint, requests a quarter, errors]
return [["/search", 24000000, 31000], ["/orders", 18000000, 26000], ["/cart", 11000000, 14000],
["/login", 6100000, 13800], ["/export", 900000, 391000], ["/health", 1000000, 0]]
def budget_rate():
# errors per 10,000 requests the quarter's budget allows
return 100
Python projection (budget.py)
def budget_tenths():
return 432
def users():
return 1000
def month_a():
return [[10, 14, 10, 420]]
def spread(n, tenths):
out = []
i = 0
while i < n:
out = out + [[i % 30 + 1, 7 * i % 24, 10, tenths]]
i = i + 1
return out
def spent(outs):
t = 0
for o in outs:
t = t + o[3]
return t
def hit(outs):
hours = [False] * 24
for o in outs:
hours[o[1]] = True
n = 0
u = 0
while u < users():
if hours[u % 24]:
n = n + 1
u = u + 1
return n
def hourly():
out = []
h = 0
while h < 24:
r = 800 + 150 * (h * 5 % 7)
if h == 3:
r = 0
out = out + [[h, r, int(r / 400)]]
h = h + 1
return out
def endpoints():
return [["/search", 24000000, 31000], ["/orders", 18000000, 26000], ["/cart", 11000000, 14000], ["/login", 6100000, 13800], ["/export", 900000, 391000], ["/health", 1000000, 0]]
def budget_rate():
return 100