ãã®è¨äºã¯ãCYBOZU SUMMER BLOG FES '25ã®è¨äºã§ãã
ããã«ã¡ã¯ãã¯ã©ã¦ãåºç¤æ¬é¨ã®åäºã§ãã
ã·ã¹ãã éçºã«ããã¦ããå ·ä½çãªå®è£ ãããæ½è±¡çãªä»æ§ããæºããã¦ãããã¨ãä¿è¨¼ãããã¨ã¯éè¦ãªèª²é¡ã§ããTLA+ ã® Refinementï¼è©³ç´°åï¼ã¯ããã®èª²é¡ã«å¯¾ãã解決çã®ä¸ã¤ã§ããæ¬è¨äºã§ã¯ã䏿¸ãå¯è½ãªãªãã¸ã§ã¯ãã¹ãã¬ã¼ã¸ã使ã£ã¦ä¸æ¸ãä¸å¯è½ãªãªãã¸ã§ã¯ãã¹ãã¬ã¼ã¸ãå®è£ ããä¾ãéãã¦ãTLA+ ã® Refinement ã®ä½¿ãæ¹ãç´¹ä»ãã¾ããã³ã¼ããå®å ¨ã«çè§£ããããã«ã¯ããç¨åº¦ã® TLA+ ã®ç¥èãå¿ è¦ã§ãããããã§ãªãã¦ãæµãã¯çè§£ã§ããããã«æ¸ããã¤ããã§ãã
TLA+ ã¨ã¯ï¼
TLA+ ã¯ãLeslie Lamport æ°ã«ãã£ã¦éçºãããã忣ã·ã¹ãã ãã¢ã«ã´ãªãºã ã®è¨è¨ã¨æ¤è¨¼ã®ããã®å½¢å¼ä»æ§è¨è¿°è¨èªã§ããTLA+ ã®ç¹å¾´ã¯ãã·ã¹ãã ã®æ¯ãèããå³å¯ã«è¨è¿°ããã¢ãã«æ¤æ»å¨ï¼TLCï¼ã使ã£ã¦èªåçã«æ¤è¨¼ã§ãããã¨ã§ããããã«ãããå®è£ åã®æ®µéã§è¨è¨ã®åé¡ãçºè¦ããä¿®æ£ãããã¨ãã§ãã¾ãã
TLA+ ã«ã¤ãã¦ããããå¦ã³ããæ¹ã«ã¯ Lamport æ°ã® Video Course ãããããã§ããå®ä¾ã交ããªããåºç¤ããå¿ç¨ã¾ã§ä½ç³»çã«å¦ã¶ãã¨ãã§ãã¾ãããã®è¨äºã®ãã¼ãã§ãã Refinement ã«ã¤ãã¦ã®ããæ£ç¢ºãã¤ãã詳細ãªå 容㮠Lecture ãããã¾ãã
TLA+ ã® Refinement ã¨ã¯ï¼
ã·ã¹ãã éçºã§ã¯ãã¾ããä½ãå®ç¾ãããããã¨ããæ½è±¡çãªä»æ§ãå®ç¾©ããæ¬¡ã«ãã©ãå®ç¾ããããã¨ããå ·ä½çãªå®è£ ãè¨è¨ãããã¨ãå¤ãã¨æãã¾ããããããå®è£ ãè¤éã«ãªãã«ã¤ãã¦ãæ¬å½ã«å ã®ä»æ§ãæºããã¦ããã確èªãããã¨ãå°é£ã«ãªãã¾ããä¾ãã°ãããã¼ã¿ã¯ä¸åº¦ã ãæ¸ãè¾¼ãããã¨ããåç´ãªä»æ§ã§ãã並è¡å¦çã忣ã·ã¹ãã ã§ã¯å®è£ ãè¤éã«ãªãããã°ãå ¥ãè¾¼ã¿ããããªãã¾ãã
Refinement ã¯ããã®åé¡ã解決ããå¼·åãªææ³ã§ããå ·ä½çãªå®è£ ãæ½è±¡çãªä»æ§ã®æ¯ãèããæ£ããå®ç¾ãã¦ãããã¨ãå½¢å¼çã«æ¤è¨¼ã§ãã¾ããå ·ä½çã«ã¯ã以ä¸ã®ãããªå©ç¹ãããã¾ãã
- 段éçãªè©³ç´°åï¼ã·ã³ãã«ãªä»æ§ããå§ãã¦ãå¾ã ã«å®è£ ã®è©³ç´°ã追å ã§ããã
- æ£ããã®ä¿è¨¼ï¼å段éã§ä»æ§ã¨ã®æ´åæ§ãèªåæ¤è¨¼ã§ããã
- è¨è¨ã®è¦éãåä¸ï¼è¤éãªå®è£ ã§ããå ã®ä»æ§ã¨ã®é¢ä¿ãæç¢ºã«ãªãã
Refinement ã®æ´»ç¨ä¾
ããããã¯ã䏿¸ãå¯è½ãªãªãã¸ã§ã¯ãã¹ãã¬ã¼ã¸ããå©ç¨ãã¦ã䏿¸ãä¸å¯è½ãªãªãã¸ã§ã¯ãã¹ãã¬ã¼ã¸ããå®è£ ããä¾ãéããRefinement ãæ´»ç¨ãã¦æ½è±¡çãªä»æ§ãæºããå®è£ ãæ®µéçã«è©³ç´°åãã¦ããæµããè¦ã¦ããã¾ãããã
æ½è±¡çãªä»æ§
ã¾ãã¯ä¸æ¸ãä¸å¯è½ãªãªãã¸ã§ã¯ãã¹ãã¬ã¼ã¸ã®ä»æ§ã決ããTLA+ ã§è¨è¿°ãã¾ãããã䏿¸ãä¸å¯è½ãªã®ã§ã許å¯ãããæä½ã¯ãªãã¸ã§ã¯ãã®ã¢ãããã¼ãã ãã¨ãã¾ããé常ããªãã¸ã§ã¯ãã¹ãã¬ã¼ã¸ã«ã¯ãã±ããããªãã¸ã§ã¯ããç¹å®ããããã®ãã¼ã®ãããªæ¦å¿µãããã¾ãããç¹å®ã®ãã±ããã®ç¹å®ã®ãã¼ã«ã ãçç®ããã¨èããããããæã«æ¸ããªããã¨ã«ãã¾ãã
ã¢ãããã¼ãã§ãããªãã¸ã§ã¯ãã¯ã©ã®ãããªãã®ã§ãããããä¾ãã° AWS S3 ã§ã¯ä¸ã¤ã®ãªãã¸ã§ã¯ãã®æå¤§ãµã¤ãºã¯5TBã ããã§ããã¤ã¾ãã¢ãããã¼ãã§ãããªãã¸ã§ã¯ãã®ç¨®é¡ã¯é«ã
æéã¨ãããã¨ã«ãªãã¾ããé«ã
æéã§ã¯ããã¾ãããã¢ãã«æ¤æ»ã§ã¯ç¾ããç¶æ
ã®æ°ãè¨ç®æéã«ç´çµãããããåãã ãã®ç¨®é¡ãã¢ãããã¼ãã§ãã仿§ãæ¤è¨¼ããã®ã¯é£ããã§ããããããã§ãæã
ã®ä»æ§ã§ã¯æãåã£ã¦ã¢ãããã¼ãã§ãããªãã¸ã§ã¯ãã¯3éãã¨ãããã¨ã«ãã¾ãããããããã d1ãd2ãd3 ã¨ãããããã®éåã Data ã¨ãã¾ãã
---- MODULE Immutable ----
CONSTANTS Data, NotFound
VARIABLES object
vars == <<object>>
Init == object = NotFound
Put(data) ==
/\ object = NotFound
/\ object' = data
Next == \E d \in Data: Put(d)
Spec == Init /\ [][Next]_vars
TypeOK == object \in {NotFound} \cup Data
Immutability ==
[][object # NotFound => object' = object]_vars
====
Immutablity ããä¸åº¦ãªãã¸ã§ã¯ããã¢ãããã¼ããããã¨ããã®ãªãã¸ã§ã¯ãã夿´ããããã¨ã¯ãªããã¨ããæ§è³ªã表ãã¦ãã¾ãããã®æ§è³ªã¯ Put(data) ã¨ããã¢ã¯ã·ã§ã³ã§ object = NotFound ã¨ããæ¡ä»¶ãæºããå ´åãã¤ã¾ãã¾ã ãªãã¸ã§ã¯ããã¢ãããã¼ãããã¦ããªãå ´åã ã object' ã夿´ããããã¨ã«ããå®ç¾ãããã¯ãã§ããå®éããã®æ§è³ªã¯ä»¥ä¸ã®ããã« Immutability ãå¸¸ã«æºãããããã¨ã確èªããããã«è¨å®ãã¦ã¢ãã«æ¤æ»ãè¡ããã¨ã§ç¢ºããããã¾ãã
SPECIFICATION Spec
CONSTANTS
Data = {d1, d2, d3}
NotFound = NotFound
INVARIANTS
TypeOK
PROPERTIES
Immutability
CHECK_DEADLOCK FALSE
ããããã¯ãã¼ã¿ã®ä¿åã«ä¸æ¸ãå¯è½ãªãªãã¸ã§ã¯ãã¹ãã¬ã¼ã¸ãå©ç¨ãã¦ãä¸è¨ã®æ½è±¡çãªä»æ§ãæºããããã°ã©ã ãå®è£ ãããã¨ãèãã¾ãããã
ä¸ã¤ã®ããã»ã¹ã®å ´å
ã¾ãã¯ä¸ã¤ã®ããã»ã¹å ã®è¤æ°ã®ã¹ã¬ããï¼t1, t2, t3ï¼ã並è¡ãã¦ãªã¯ã¨ã¹ããå¦çããå ´åãèãã¾ãããã®å®è£ ã§ãã¼ã¿ã®ä¿åã«å©ç¨ãã¦ããã¹ãã¬ã¼ã¸ã¯ä¸æ¸ããå¯è½ã§ããã䏿¸ãããªãããã«ããããã«ã¯ãã§ã«ãªãã¸ã§ã¯ããã¢ãããã¼ãããã¦ãããã©ããã確ããã¦ãããã¢ãããã¼ãããã¦ããªãå ´åã®ã¿ã¢ãããã¼ãããã¨ãããã¨ãå¿ è¦ããã§ãã
以ä¸ã«ãã®å®è£
ã®ä»æ§ã示ãã¾ãã䏿¸ãå¯è½ãªãªãã¸ã§ã¯ãã¹ãã¬ã¼ã¸ã store ã¨ããååã§å©ç¨ãã¦ãã¾ãã
---- MODULE SingleProcess ----
CONSTANTS Data, NotFound, Threads
VARIABLES pc, store
vars == <<pc, store>>
Init ==
/\ pc = [ t \in Threads |-> "accept" ]
/\ store = NotFound
CheckStoreOK(t) ==
/\ pc[t] = "accept"
/\ store = NotFound
/\ pc' = [ pc EXCEPT ![t] = "checked_store" ]
/\ UNCHANGED store
CheckStoreFail(t) ==
/\ pc[t] = "accept"
/\ store # NotFound
/\ pc' = [ pc EXCEPT ![t] = "done" ]
/\ UNCHANGED store
Put(t, d) ==
/\ pc[t] = "checked_store"
/\ pc' = [ pc EXCEPT ![t] = "done" ]
/\ store' = d
Terminated ==
/\ \A t \in Threads: pc[t] = "done"
/\ UNCHANGED vars
Next ==
\E t \in Threads:
\/ CheckStoreOK(t)
\/ CheckStoreFail(t)
\/ \E d \in Data: Put(t, d)
\/ Terminated
Spec == Init /\ [][Next]_vars
TypeOK == store \in {NotFound} \cup Data
====
ãã®å®è£
ãæ½è±¡çãªä»æ§ãæºããã¦ãããæ¤è¨¼ãã¦ã¿ã¾ããããä¸ã¤ã®æ¹æ³ã¯æ½è±¡çãªä»æ§ãæºããã¦ããæ§è³ªï¼ä¾ãã° Immutablityï¼ããã®å®è£
ãæºããã¦ãããã©ãã調ã¹ãã¨ãããã®ãããã¾ããããããæ½è±¡çãªä»æ§ãæºããã¹ãæ§è³ªã®æ°ãå¤ãå ´åã¯ã©ãã§ããããã仿§ãæºããã¦ããæ§è³ªãå
¨ã¦å®è£
ã§ãæºãããã©ããã調ã¹ãã®ã¯å¤§å¤ã«ãªãã¾ãããäºåº¦æéã«ãæãã¾ãã
ããã§ä¾¿å©ãªã®ã Refinement ã§ãããã®ææ³ã§ã¯ãå®è£ ãæ½è±¡çãªä»æ§ã®è©³ç´°åã«ãªã£ã¦ãããã©ããã調ã¹ããã¨ãã§ãã¾ãã
object == store Immutable == INSTANCE Immutable WITH object <- object Refinement == Immutable!Spec
ãã®ã³ã¼ãã®æå³ã¯ä»¥ä¸ã®éãã§ãã
object == storeï¼å®è£ ã®store夿°ãæ½è±¡ä»æ§ã®object夿°ã«ãããã³ã°ãããINSTANCE Immutable WITH object <- objectï¼æ½è±¡ä»æ§ã¢ã¸ã¥ã¼ã«ãã¤ã³ãã¼ããã夿°ããããã³ã°ãããRefinement == Immutable!Specï¼å®è£ ãæ½è±¡ä»æ§Specãæºãããã¨ã表ãããããã£ãå®ç¾©ããã
Refinement ããããã£ãæ¤è¨¼ãããã¨ã§ãå®è£
ã®ãã¹ã¦ã®å®è¡ãã¹ãæ½è±¡ä»æ§ã§è¨±å¯ãããå®è¡ãã¹ã«å¯¾å¿ãããã¨ã確èªã§ãã¾ãã
SPECIFICATION Spec
CONSTANTS
Data = {d1, d2, d3}
NotFound = NotFound
Threads = {t1, t2, t3}
INVARIANTS
TypeOK
PROPERTIES
Refinement
å®è¡ããã¨ã以ä¸ã®ããã« Immutable ã®æ§è³ªãæºããããªãã¨ãããã¨ã¨ããã®åä¾ã示ããã¾ãã1

ãã®åä¾ã§ã¯ä»¥ä¸ã®æµãã§æ½è±¡çãªä»æ§ã§ã¯èµ·ãããªãã£ããã¨ãèµ·ãã£ã¦ãã¾ãã
- ã¹ã¬ãã t1 ã
storeã確èªããã¢ãããã¼ãããã¦ããªãã¨ãããã - ã¹ã¬ãã t2 ã
storeã確èªããã¢ãããã¼ãããã¦ããªãã¨ãããã - ã¹ã¬ãã t1 ã
storeã«d1ãã¢ãããã¼ãããã - ã¹ã¬ãã t2 ã
storeã«d2ãã¢ãããã¼ãããã
ããã¯å
¸åç㪠TOCTOU åé¡ã§ããããã§ãã¯æç¹ã§ã¯æ¡ä»¶ãæºããã¦ãã¦ããä½¿ç¨æç¹ã§ã¯å¥ã®ã¹ã¬ããã«ãã£ã¦ç¶æ
ã夿´ããã¦ããå¯è½æ§ãããã¾ããä»ã¯ä¸ã¤ã®ããã»ã¹å
ã§åä½ããè¤æ°ã®ã¹ã¬ãããèãã¦ããã®ã§ãstore ãèªã¿æ¸ãããåã«ããã»ã¹å
ã§ããã¯ãåãã¨ããããã§ãããã®ããã«æ¹ä¿®ãããã®ã次ã®å®è£
ã§ãã
-VARIABLES pc, store -vars == <<pc, store>> +VARIABLES pc, lock, store +vars == <<pc, lock, store>> Init == /\ pc = [ t \in Threads |-> "accept" ] + /\ lock = FALSE /\ store = NotFound -CheckStoreOK(t) == +AcquireLock(t) == /\ pc[t] = "accept" + /\ lock = FALSE + /\ pc' = [ pc EXCEPT ![t] = "acquired_lock" ] + /\ lock' = TRUE + /\ UNCHANGED store + +CheckStoreOK(t) == + /\ pc[t] = "acquired_lock" /\ store = NotFound /\ pc' = [ pc EXCEPT ![t] = "checked_store" ] - /\ UNCHANGED store + /\ UNCHANGED <<store, lock>> CheckStoreFail(t) == - /\ pc[t] = "accept" + /\ pc[t] = "acquired_lock" /\ store # NotFound /\ pc' = [ pc EXCEPT ![t] = "done" ] + /\ lock' = FALSE /\ UNCHANGED store Put(t, d) == /\ pc[t] = "checked_store" /\ pc' = [ pc EXCEPT ![t] = "done" ] + /\ lock' = FALSE /\ store' = d
Next ==
\E t \in Threads:
+ \/ AcquireLock(t)
\/ CheckStoreOK(t)
\/ CheckStoreFail(t)
\/ \E d \in Data: Put(t, d)
ãããå ã»ã©ã¨åãè¨å®ãã¡ã¤ã«ã§æ¤è¨¼ããã¨ãã¨ã©ã¼ã¯å ±åãããªããªãã¾ãããã®å®è£ ãæ½è±¡çãªä»æ§ã¨åãæ¯ãèãããããã¨ã確ããããã¾ããã
è¤æ°ã®ããã»ã¹ã®å ´å
å®ç°å¢ã§éç¨ããå ´åãã¹ã±ã¼ã©ããªãã£ãèé害æ§ãèããã¨ãè¤æ°ã®ããã»ã¹ã«å¦çã忣ã§ããã¨ããã§ããããããããããã»ã¹ãè¤æ°ã«ãªãã¨ãä¸è¬ã«ããã»ã¹å ã®ããã¯ã«é ¼ããã¨ãã§ããªããªãã¾ããä»£æ¿æ¡ã¯ããããããããã§ãããããã§ ACID ç¹æ§ãæã¤ãã¼ã¿ãã¼ã¹ã®ãã©ã³ã¶ã¯ã·ã§ã³ã使ãä¾ãèãã¦ã¿ã¾ãããã
以ä¸ã®ãããªã¢ã«ã´ãªãºã ã§ã¢ãããã¼ããè¡ããã¨ãèãã¾ããç°¡åã®ãããåããã»ã¹ã¯ä¸ã¤ã ãã®ãªã¯ã¨ã¹ããå¦çãããã¨ã«ãã¾ãã
- åããã»ã¹ã¯ã¢ãããã¼ã対象ã®ãªãã¸ã§ã¯ãã䏿¸ãå¯è½ãªãªãã¸ã§ã¯ãã¹ãã¬ã¼ã¸ã®ä¸æé åï¼ãªã¯ã¨ã¹ãæ¯ã«åºæãªå ´æï¼ã«ã¢ãããã¼ãããã
- ãã¼ã¿ãã¼ã¹ã®ãã©ã³ã¶ã¯ã·ã§ã³å
ã§ä»¥ä¸ãå®è¡ããã
- ã¾ã ã³ãããããã¦ããªããã°ããã®ããã»ã¹ã®ä¸æé åãæ£å¼ãªä¿åå ´æã¨ãã¦è¨é²ããã³ãããããã
- ãã§ã«ã³ãããããã¦ããã°ããã©ã³ã¶ã¯ã·ã§ã³ã䏿¢ãã䏿é åãã¯ãªã¼ã³ã¢ããããã
ãã®ã¢ããã¼ãã«ãããè¤æ°ã®ããã»ã¹ãåæã«ãªãã¸ã§ã¯ããã¢ãããã¼ããããã¨ãã¦ãããã¼ã¿ãã¼ã¹ã®ãã©ã³ã¶ã¯ã·ã§ã³æ©è½ã«ããä¸ã¤ã ããæåãããã¨ãä¿è¨¼ããã¾ãã
以ä¸ã«ãã®ä»æ§ã«å¯¾å¿ãã TLA+ ã®ã³ã¼ãã示ãã¾ãã
---- MODULE Distributed ----
CONSTANTS Data, NotFound, Processes
VARIABLES pc, database, store
vars == <<pc, database, store>>
Init ==
/\ pc = [ p \in Processes |-> "accept" ]
/\ database = [ committed |-> FALSE ]
/\ store = [ p \in Processes |-> NotFound ]
CreateTemp(p, d) ==
/\ pc[p] = "accept"
/\ pc' = [ pc EXCEPT ![p] = "created_temp" ]
/\ store' = [ store EXCEPT ![p] = d ]
/\ UNCHANGED database
Commit(p) ==
/\ pc[p] = "created_temp"
/\ ~database.committed
/\ pc' = [ pc EXCEPT ![p] = "done" ]
/\ database' = [ committed |-> TRUE, process |-> p ]
/\ UNCHANGED store
Abort(p) ==
/\ pc[p] = "created_temp"
/\ database.committed
/\ pc' = [ pc EXCEPT ![p] = "done" ]
/\ store' = [ store EXCEPT ![p] = NotFound ]
/\ UNCHANGED database
Terminated ==
/\ \A p \in Processes: pc[p] = "done"
/\ UNCHANGED vars
Next ==
\E p \in Processes:
\/ \E d \in Data: CreateTemp(p, d)
\/ Commit(p)
\/ Abort(p)
\/ Terminated
Spec == Init /\ [][Next]_vars
TypeOK == \A p \in Processes: store[p] \in {NotFound} \cup Data
ãã®ã¨ããããã¼ã¿ãã¼ã¹ã§ã³ãããæ¸ã¿ã§ããã°ä¿åããããªãã¸ã§ã¯ãã®å ´æã«ãããªãã¸ã§ã¯ããã¢ãããã¼ãæ¸ã¿ãã³ãããæ¸ã¿ã§ãªããã°ã¾ã ã¢ãããã¼ãããã¦ããªããã¨ã¿ãªãã°æ½è±¡çãªä»æ§ãæºãããã§ããããããã®å ´åã® Refinement ã¯ä¸ã¤ã®ããã»ã¹ã®å ´åã®ããã«åç´ãªãããã³ã°ã§ã¯ããã¾ãããã以ä¸ã®ããã«æ¸ããã¨ãã§ãã¾ãã
object ==
IF database.committed
THEN store[database.process]
ELSE NotFound
Immutable == INSTANCE Immutable WITH object <- object
Refinement == Immutable!Spec
ãã®ä»æ§ã以ä¸ã®è¨å®ãã¡ã¤ã«ã§æ¤è¨¼ãããã¨ã§ãæ½è±¡çãªä»æ§ãæºãããã¨ã確èªã§ãã¾ãã
SPECIFICATION Spec
CONSTANTS
Data = {d1, d2, d3}
NotFound = NotFound
Processes = {p1, p2, p3}
INVARIANTS
TypeOK
PROPERTIES
Refinement
ã¾ã¨ã
ãã®è¨äºã§ã¯ TLA+ ã® Refinement ã使ã£ã¦ãæ½è±¡çãªä»æ§ãæºããå®è£ ãæ®µéçã«è©³ç´°åãã¦ããææ³ã«ã¤ãã¦ç´¹ä»ãã¾ãããå ·ä½çã«ã¯ä»¥ä¸ã®æµãã§é²ãã¾ããã
- æ½è±¡ä»æ§ã®å®ç¾©ï¼ã¤ãã¥ã¼ã¿ãã«ã¹ãã¬ã¼ã¸ã®æ¬è³ªçãªæ§è³ªãè¨è¿°
- åä¸ããã»ã¹ã§ã®å®è£ ï¼TOCTOUåé¡ã®çºè¦ã¨ããã¯ã«ãã解決
- 忣ã·ã¹ãã ã§ã®å®è£ ï¼ãã¼ã¿ãã¼ã¹ãã©ã³ã¶ã¯ã·ã§ã³ã使ã£ãå®è£
Refinement ãæ´»ç¨ããåå®è£ 段éã§æ£ãããä¿è¨¼ããªããã·ã¹ãã ãè¤éåãã¦ããã¤ã¡ã¼ã¸ããä¼ãã§ãã¦ããã°å¹¸ãã§ãã
- ãã®ã¹ã¯ãªã¼ã³ã·ã§ãã㯠Visual Studio Code ã® TLA+ (Temporal Logic of Actions) ã¨ããæ¡å¼µãå©ç¨ãã¦ã¢ãã«æ¤æ»ãè¡ã使ãã¾ããã↩