quorate/docs/spec/project-delivery/Qualification.tla

205 lines
7.3 KiB
Text
Raw Permalink Normal View History

---- MODULE Qualification ----
EXTENDS Integers, FiniteSets
CONSTANTS Deliveries, MaxBuilds, MaxRequests, MaxTime, Interval, InitialPolicy
Policies == {"OnBuild", "ManualOnly"}
Keys == Deliveries \X {"Auto"} \X (1..MaxBuilds) \cup
Deliveries \X {"Manual"} \X (1..MaxRequests)
Phases == {"Absent", "Pending", "Reserved", "Queued", "Running", "Terminal"}
ActivePhases == {"Reserved", "Queued", "Running"}
VARIABLES latest, policy, request, attempts, now, lastStart, covered
vars == <<latest, policy, request, attempts, now, lastStart, covered>>
ASSUME /\ Deliveries /= {}
/\ MaxBuilds \in Nat \ {0}
/\ MaxRequests \in Nat \ {0}
/\ MaxTime \in Nat
/\ Interval \in [Deliveries -> Nat]
/\ InitialPolicy \in [Deliveries -> Policies]
Input(delivery, build) == <<delivery, build>>
Inputs == Deliveries \X (0..MaxBuilds)
EmptyRequest == [build |-> 0, nonce |-> 0]
EmptyAttempt(delivery) ==
[phase |-> "Absent", build |-> 0, input |-> Input(delivery, 0),
started |-> -1, prior |-> -1]
Active == {key \in Keys : attempts[key].phase \in ActivePhases}
Due(delivery) ==
lastStart[delivery] = -1 \/ now >= lastStart[delivery] + Interval[delivery]
TypeOK ==
/\ latest \in [Deliveries -> 0..MaxBuilds]
/\ policy \in [Deliveries -> Policies]
/\ request \in [Deliveries -> [build : 0..MaxBuilds, nonce : 0..MaxRequests]]
/\ attempts \in [Keys -> [phase : Phases, build : 0..MaxBuilds,
input : Inputs, started : -1..MaxTime,
prior : -1..MaxTime]]
/\ now \in 0..MaxTime
/\ lastStart \in [Deliveries -> -1..MaxTime]
/\ covered \in [Deliveries -> 0..MaxBuilds]
Init ==
/\ latest = [delivery \in Deliveries |-> 0]
/\ policy = InitialPolicy
/\ request = [delivery \in Deliveries |-> EmptyRequest]
/\ attempts = [key \in Keys |-> EmptyAttempt(key[1])]
/\ now = 0
/\ lastStart = [delivery \in Deliveries |-> -1]
/\ covered = [delivery \in Deliveries |-> 0]
BuildSuccess(delivery) ==
/\ latest[delivery] < MaxBuilds
/\ latest' = [latest EXCEPT ![delivery] = @ + 1]
/\ UNCHANGED <<policy, request, attempts, now, lastStart, covered>>
ChangePolicy(delivery, value) ==
/\ policy[delivery] /= value
/\ policy' = [policy EXCEPT ![delivery] = value]
/\ UNCHANGED <<latest, request, attempts, now, lastStart, covered>>
Annotate(delivery, build, nonce) ==
/\ build <= latest[delivery]
/\ request' = [request EXCEPT ![delivery] = [build |-> build, nonce |-> nonce]]
/\ UNCHANGED <<latest, policy, attempts, now, lastStart, covered>>
Accept(delivery) ==
LET value == request[delivery]
key == <<delivery, "Manual", value.nonce>>
IN /\ value.build > 0
/\ value.nonce > 0
/\ attempts[key].phase = "Absent"
/\ attempts' = [attempts EXCEPT ![key] =
[EmptyAttempt(delivery) EXCEPT
!.phase = "Pending", !.build = value.build,
!.input = Input(delivery, value.build)]]
/\ UNCHANGED <<latest, policy, request, now, lastStart, covered>>
ReserveManual(key) ==
/\ attempts[key].phase = "Pending"
/\ Active = {}
/\ attempts' = [attempts EXCEPT ![key].phase = "Reserved"]
/\ UNCHANGED <<latest, policy, request, now, lastStart, covered>>
ReserveAuto(delivery) ==
LET build == latest[delivery]
key == <<delivery, "Auto", build>>
IN /\ policy[delivery] = "OnBuild"
/\ build > covered[delivery]
/\ Due(delivery)
/\ Active = {}
/\ attempts[key].phase = "Absent"
/\ attempts' = [attempts EXCEPT ![key] =
[EmptyAttempt(delivery) EXCEPT
!.phase = "Reserved", !.build = build,
!.input = Input(delivery, build)]]
/\ UNCHANGED <<latest, policy, request, now, lastStart, covered>>
Submit(key) ==
/\ attempts[key].phase = "Reserved"
/\ attempts' = [attempts EXCEPT ![key].phase = "Queued"]
/\ UNCHANGED <<latest, policy, request, now, lastStart, covered>>
Start(key) ==
LET delivery == key[1]
build == attempts[key].build
IN /\ attempts[key].phase = "Queued"
/\ attempts' = [attempts EXCEPT ![key].phase = "Running",
![key].started = now, ![key].prior = lastStart[delivery]]
/\ lastStart' = [lastStart EXCEPT ![delivery] = now]
/\ covered' = [covered EXCEPT ![delivery] =
IF build > @ THEN build ELSE @]
/\ UNCHANGED <<latest, policy, request, now>>
Complete(key) ==
/\ attempts[key].phase \in {"Queued", "Running"}
/\ attempts' = [attempts EXCEPT ![key].phase = "Terminal"]
/\ UNCHANGED <<latest, policy, request, now, lastStart, covered>>
Tick ==
/\ now < MaxTime
/\ now' = now + 1
/\ UNCHANGED <<latest, policy, request, attempts, lastStart, covered>>
Next ==
\/ \E delivery \in Deliveries : BuildSuccess(delivery)
\/ \E delivery \in Deliveries, value \in Policies : ChangePolicy(delivery, value)
\/ \E delivery \in Deliveries, build \in 1..MaxBuilds, nonce \in 1..MaxRequests :
Annotate(delivery, build, nonce)
\/ \E delivery \in Deliveries : Accept(delivery)
\/ \E key \in Keys : ReserveManual(key)
\/ \E delivery \in Deliveries : ReserveAuto(delivery)
\/ \E key \in Keys : Submit(key)
\/ \E key \in Keys : Start(key)
\/ \E key \in Keys : Complete(key)
\/ Tick
Spec == Init /\ [][Next]_vars
FairSpec ==
/\ Spec
/\ \A key \in Keys :
/\ SF_vars(ReserveManual(key))
/\ WF_vars(Submit(key))
/\ WF_vars(Start(key))
/\ WF_vars(Complete(key))
/\ \A delivery \in Deliveries :
/\ WF_vars(Accept(delivery))
/\ SF_vars(ReserveAuto(delivery))
/\ WF_vars(Tick)
ManualProgress ==
\A key \in Keys :
(attempts[key].phase = "Pending") ~> (attempts[key].phase = "Terminal")
Eligible(delivery, build) ==
/\ policy[delivery] = "OnBuild"
/\ latest[delivery] = build
/\ build > covered[delivery]
/\ Due(delivery)
AutoProgress ==
\A delivery \in Deliveries, build \in 1..MaxBuilds :
Eligible(delivery, build) ~>
(attempts[<<delivery, "Auto", build>>].phase /= "Absent" \/
~Eligible(delivery, build))
GlobalCap == Cardinality(Active) <= 1
SuccessfulInputs ==
\A key \in Keys : attempts[key].phase /= "Absent" =>
/\ attempts[key].build \in 1..latest[key[1]]
/\ attempts[key].input = Input(key[1], attempts[key].build)
AutoInterval ==
\A key \in Keys :
/\ key[2] = "Auto"
/\ attempts[key].started >= 0
/\ attempts[key].prior >= 0
=> attempts[key].started >= attempts[key].prior + Interval[key[1]]
InputsStable ==
[][\A key \in Keys : attempts[key].phase /= "Absent" =>
UNCHANGED <<attempts[key].build, attempts[key].input>>]_vars
AutoSelection ==
[][\A key \in Keys :
/\ key[2] = "Auto"
/\ attempts[key].phase = "Absent"
/\ attempts'[key].phase = "Reserved"
=> /\ policy[key[1]] = "OnBuild"
/\ attempts'[key].build = latest[key[1]]
/\ latest[key[1]] > covered[key[1]]
/\ Due(key[1])]_vars
NoRestart ==
[][\A key \in Keys : attempts[key].phase = "Terminal" =>
attempts'[key].phase = "Terminal"]_vars
OnBuildPolicy == [delivery \in Deliveries |-> "OnBuild"]
TwoTicks == [delivery \in Deliveries |-> 2]
====