scieee AI-readable full text Open interactive document viewer

SOSP '25 Artifacts and Proofs - Running Consistent Applications Closer to Users with Radical for Lower Latency

Kaashoek, Nicolaas

Abstract

This record contains the code for reproducing the experiments detailed in our SOSP 2025 Paper, as well as the protocol proofs for both the replicated and single server version of the system.

Full text

Running Consistent Applications Closer to Users with Radical for Lower Latency 1 Consistency Proof 1.1 Definitions Here we show that Radical provides linearizability guarantees to its users. Linearizability is a local consistency model, thus to prove that a system is linearizable, it is sufficient to show that operations on each individual key are linearizable [ 1 ]. Without loss of generality, consider key π‘˜ . Radical supports two operations: reads ( π‘Ÿ(π‘˜π‘£) denoting that a read to key π‘˜ returns its value version 𝑣 ) and writes ( 𝑀(π‘˜π‘£) denoting a write to π‘˜ of value version 𝑣 ). Linearizability requires that there exists a total order of operations on π‘˜ which we construct as follows: 1. Each write is ordered by its version number: 𝑀(π‘˜π‘£)𝑒π‘₯𝑒 βˆ’βˆ’β†’ 𝑀(π‘˜π‘£+𝑖),𝑖 >0. 2. Each read is ordered after the write that it observes: 𝑀(π‘˜π‘£)𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘Ÿ(π‘˜π‘£) 3. All reads that observe the same write are ordered by invocation time: π‘Ÿ1(π‘˜π‘£)𝑒π‘₯𝑒 βˆ’βˆ’β†’ ... 𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘Ÿπ‘›(π‘˜π‘£) if π‘Ÿ1.𝑖𝑛𝑣 <... < π‘Ÿπ‘›.𝑖𝑛𝑣 For the system to be linearizable, this total order must also obey real-time ordering constraints. 1.2 Proof Thus to show that Radical is linearizable, we must show that Radical respects the above-constructed total order under real-time constraints. First, let all operation be illustrated as a directed graph where the operations are nodes that are connected by real-time edges. Then we can say that there exists a total real-time order if and only if the directed graph is acyclic (operations do not circularly affect each other), meaning that the following invariant holds: βˆ€π‘œπ‘π‘–, π‘œπ‘π‘—,(π‘œπ‘π‘– π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘œπ‘π‘—)=β‡’ Β¬(π‘œπ‘π‘— π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘œπ‘π‘–). Note that there exists a real-time edge π‘œπ‘1 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘œπ‘2 if π‘œπ‘2 sees the result of π‘œπ‘1 and π‘œπ‘2 starts after π‘œπ‘1 ends ( π‘œπ‘1.π‘Ÿπ‘’π‘ π‘ < π‘œπ‘2.𝑖𝑛𝑣 ). Note that this implies that operations are transitive. That is if π‘œπ‘1 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘œπ‘2 and π‘œπ‘2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘œπ‘3 , then π‘œπ‘1 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘œπ‘3 . Two operations have a real-time edge in one of two cases: 1. π‘œπ‘1 and π‘œπ‘2 are performed sequentially by the same function execution where π‘œπ‘1precedes π‘œπ‘2 2. π‘œπ‘1 and π‘œπ‘2 are performed by two different executions, and π‘œπ‘2sees the result of π‘œπ‘1via Radical’s design We prove that Radical’s total order is a real-time order by contradiction. More specifically, we consider pairs of operations (𝑀,𝑀),(π‘Ÿ,π‘Ÿ),(π‘Ÿ, 𝑀),(𝑀, π‘Ÿ). 1. (𝑀,𝑀) :let there be two writes such that 𝑀1=𝑀(π‘˜π‘£)𝑒π‘₯𝑒 βˆ’βˆ’β†’ 𝑀2=𝑀(π‘˜π‘£β€²), 𝑣′>𝑣 . By the contradiction we assume we also have 𝑀2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ 𝑀1 . Because 𝑀2 is real-time ordered before another operation, we know 𝑀2 must have completed. There are two cases: a. 𝑀2 completed at the edge. Then it must have acquired a write lock as part of its successful consistency check (lines XX–YY). This lock precludes any other operation from acquiring a write lock (lines XX–YY) until it is released. There are three subcases depending on how 𝑀1executes: i. 𝑀1 completes at the edge. Then it must also acquire a write lock as part of its successful consistency check. Since 𝑀2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ 𝑀1 , then 𝑀2.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ < 𝑀2.π‘Ÿπ‘’π‘ π‘ <𝑀1.𝑖𝑛𝑣 <𝑀1.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ , so we know that 𝑀1 must acquire its lock after 𝑀2 releases its lock. Version numbers only increase for subsequent writes (lines XX–YY), so 𝑣′ (written by 𝑀2 ) <𝑣(written by 𝑀1). Contradiction. ii. 𝑀1 completes in the datacenter. Then it must also acquire a write lock as part of its failed consistency check. 𝑀2 must have released the locks before 𝑀1 could start running at the datacenter, so if 𝑀2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ 𝑀1 , then 𝑀2.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ <𝑀2.π‘Ÿπ‘’π‘ π‘ < 𝑀1.𝑖𝑛𝑣 <𝑀1.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ , similarly to the case when 𝑀1 completes in the edge. Version numbers only increase for subsequent writes, so 𝑣′<𝑣 . Contradiction. iii. 𝑀2 completes at the datacenter or at the edge as part of a timeout. If 𝑀2 started running at the edge, then it must have acquired a write lock as part of its consistency check. If the datacenter times out on the follow-up from the edge, the write lock is still held, and the execution is rerun in the datacenter. Thus, depending on whether the follow-up from the edge finally arrives or the function on the datacenter finishes executing first, the same logic from cases (i) and (ii) applies. If the function finished executing first, the later stale follow-up is discarded, and if the follow-up arrives during datacenter execution, the result of the datacenter execution is ignored. b. 𝑀2 completes in the datacenter. Then it must have acquired a write lock as part of its failed consistency check (lines XX-YY). No other operations, whether in the datacenter or at the edge, could be performing operations on π‘˜ until 𝑀2 completes. There are two 1 subcases depending on where 𝑀1 executes (we omit the timeout case as the reasoning is similar to what is described in 1(a)iii): i. 𝑀1 completes at the edge. Then it must have been the case that 𝑀1 acquired the write lock as part of its successful consistency check. This necessitates that 𝑀2 released its lock first. If 𝑀2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ 𝑀1 , then 𝑀2.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ <𝑀2.π‘Ÿπ‘’π‘ π‘ <𝑀1.𝑖𝑛𝑣 < 𝑀1.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ . Version numbers only increase for subsequent writes, so 𝑣′<𝑣. Contradiction. ii. 𝑀1 completes at the datacenter. Then it must have waited for 𝑀2 to release its lock before acquiring the lock as part of its failed consistency check. Thus, 𝑀2 and 𝑀1 execute at the datacenter sequentially, in that order. As in the above case, 𝑀2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ 𝑀1=⇒𝑀2.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ <𝑀2.π‘Ÿπ‘’π‘ π‘ < 𝑀1.𝑖𝑛𝑣 <𝑀1.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ , and since version numbers only increase for subsequent writes, so 𝑣′<𝑣 . Contradiction. 2. (π‘Ÿ,π‘Ÿ) :let there be two reads such that π‘Ÿ1 𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘Ÿ2 . By the contradiction we assume we also have π‘Ÿ2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘Ÿ1 . Because π‘Ÿ2 is real-time ordered before another operation, we know π‘Ÿ2must have completed. There are two cases: a. If π‘Ÿ1=π‘Ÿ(π‘˜π‘£) and π‘Ÿ2=π‘Ÿ(π‘˜π‘£) return the same values, then the two reads are ordered by invocation time. Since π‘Ÿ1.𝑖𝑛𝑣 <π‘Ÿ2.𝑖𝑛𝑣 , there cannot be a real-time edge from π‘Ÿ2to π‘Ÿ1. Contradiction. b. If π‘Ÿ1=π‘Ÿ(π‘˜π‘£) and π‘Ÿ2=π‘Ÿ(π‘˜π‘£β€²) where 𝑣′>𝑣 and π‘Ÿ1 𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘Ÿ2 , then there must exist 𝑀1 that π‘Ÿ1 sees and 𝑀2 that π‘Ÿ2 sees, such that 𝑀2 is ordered after 𝑀1 . In other words, it must be the case that 𝑀1 𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘Ÿ1 , 𝑀2 𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘Ÿ2 , and 𝑀1 𝑒π‘₯𝑒 βˆ’βˆ’β†’ 𝑀2 . Assuming there exists the edge π‘Ÿ2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘Ÿ1 , then we know that π‘Ÿ2 must have completed. There are two cases: i. If π‘Ÿ2 completed at the edge, it must be the case that the read lock on π‘˜ was acquired and the consistency check was successful. Thus, there could have been no writes to π‘˜ between π‘Ÿ2.𝑖𝑛𝑣 and π‘Ÿ2.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ . There are two subcases depending on where π‘Ÿ1executes: A. π‘Ÿ1 also completes at the edge. Then it must have also acquired a read lock on π‘˜ to ensure all pending writes were complete. However, π‘Ÿ1 would have acquired the read lock after 𝑀1 executed. Since 𝑀1 should have acquired a write lock before π‘Ÿ1 , it is necessary that 𝑀1 executed between π‘Ÿ2.π‘Ÿπ‘’π‘Žπ‘‘_π‘’π‘›π‘™π‘œπ‘π‘˜ and π‘Ÿ1.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ . In other words, the following must be true: π‘Ÿ2.𝑖𝑛𝑣 <π‘Ÿ2.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ <𝑀1.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ < π‘Ÿ1.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ . Since π‘Ÿ2.π‘Ÿπ‘’π‘ π‘ <π‘Ÿ1.𝑖𝑛𝑣 by assumption ( π‘Ÿ2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘Ÿ1 ) and 𝑀2.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ < π‘Ÿ2.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ ( 𝑀2 𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘Ÿ2 ), then transitively, it must be true that 𝑀2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘Ÿ1 . Since reads must be ordered after the writes they observe, 𝑀1 must occur between 𝑀2 and π‘Ÿ1 . However, writes are ordered in increasing version number in the total order, and since 𝑀2=𝑀(π‘˜π‘£β€²),𝑀1= 𝑀(π‘˜π‘£), 𝑣′>𝑣, this is a contradiction. B. π‘Ÿ1 completes at the datacenter. Then it must have acquired a read lock on π‘˜ to avoid reading stale data. Similarly, 𝑀1 must have acquired the write lock at some point before π‘Ÿ1 acquired its read lock. Similar to the above, we necessarily expect that π‘Ÿ2.𝑖𝑛𝑣 <π‘Ÿ2.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ < 𝑀1.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ <π‘Ÿ1.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ and 𝑀2.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ < π‘Ÿ2.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ . Thus 𝑀2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘Ÿ1=⇒𝑀2 𝑒π‘₯𝑒 βˆ’βˆ’β†’ 𝑀1 𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘Ÿ1 which violates legal ordering of writes by version number. Contradiction. ii. If π‘Ÿ2 completed at the datacenter, then the read lock on π‘˜ was acquired as part of a failed consistency check. No executions that write to π‘˜ are possible (whether at datacenter or on edge) once the read lock is acquired. As described above, regardless of where π‘Ÿ1 executes, 𝑀1 must have obtained a write lock beforehand such that 𝑀2.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ < π‘Ÿ2.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ <𝑀1.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ <π‘Ÿ1.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ . Consequently, 𝑀2 π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘Ÿ1 , 𝑀2 𝑒π‘₯𝑒 βˆ’βˆ’β†’ 𝑀1 𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘Ÿ1 which is out of order writes. Contradiction. 3. (π‘Ÿ,𝑀) :let there be a read and a write such that π‘Ÿ=π‘Ÿ(π‘˜π‘£)𝑒π‘₯𝑒 βˆ’βˆ’β†’ 𝑀′=𝑀(π‘˜π‘£β€²), 𝑣′>𝑣 . By the contradiction we also have π‘€β€²π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘Ÿ . Because 𝑀′ is real-time ordered before another operation, we know 𝑀′ must have completed. There are two cases: a. 𝑀′ completes at the edge. Then it must have acquired a write lock on π‘˜ before π‘Ÿ executed. Whether π‘Ÿ was executed on edge or in datacenter, it held the read lock and sent a response back to the user before 𝑀′ took its write lock. In other words, since π‘€β€²π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘Ÿ , then 𝑀′.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ <𝑀′.π‘Ÿπ‘’π‘ π‘ <π‘Ÿ.𝑖𝑛𝑣 <π‘Ÿ.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ . However, π‘Ÿ would return the value of π‘˜ of version 𝑣 , not 𝑣′ . Thus there must exist a write 𝑀=𝑀(π‘˜π‘£) that is ordered between 𝑀′ and π‘Ÿ such that 𝑀′𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘€π‘Ÿ βˆ’β†’ , which are out-of-order writes. Contradiction. b. 𝑀′ completed at the datacenter. Then it must have acquired a write lock as part of its failed consistency check. Then 𝑀 , by the same logic as above, either executes in datacenter or on edge, after 𝑀′ released its lock, so if π‘€β€²π‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ π‘Ÿ , the operations must be ordered as 𝑀′𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘€π‘Ÿ βˆ’β†’ , guaranteed by 𝑀′.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ < 𝑀.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ <π‘Ÿ.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ , which are out-of-order writes. Contradiction. 2 4. (𝑀, π‘Ÿ) :let there be a write and a read that observes that write such that 𝑀=𝑀(π‘˜π‘£)𝑒π‘₯𝑒 βˆ’βˆ’β†’ π‘Ÿ=π‘Ÿ(π‘˜π‘£) . By the contradiction we also have π‘Ÿπ‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ 𝑀 . Because π‘Ÿ is real-time ordered before another operation, we know π‘Ÿ must have completed. There are two cases: a. π‘Ÿ completes at the edge. Then it must have acquired a read lock on π‘˜ . There are then two subcases on where 𝑀is executed: i. If 𝑀 is executed on the edge, then it acquired the write lock and necessarily after π‘Ÿ released its read lock. Thus π‘Ÿ must have read some data before 𝑀 executed. Since π‘Ÿ read π‘˜π‘£ before π‘˜π‘£ was written by 𝑀 , the read is not ordered by the write it observes. Contradiction. ii. If 𝑀 executed in the datacenter, then similarly π‘Ÿ.𝑖𝑛𝑣 <π‘Ÿ.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ <π‘Ÿ.π‘Ÿπ‘’π‘ π‘ <𝑀.𝑖𝑛𝑣 <𝑀.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ which means that the read is not ordered after the write it observes. Contradiction. b. π‘Ÿ completes at the datacenter as a result of a failed consistency check. It does so after acquiring a read lock such that all pending writes complete first. Consider where 𝑀is executed afterwards: i. If 𝑀 is executed at the edge, then 𝑀 acquired a write lock as part of a successful consistency check, thereby requiring that π‘Ÿ.π‘Ÿπ‘’π‘Žπ‘‘_π‘™π‘œπ‘π‘˜ <𝑀.π‘€π‘Ÿπ‘–π‘‘π‘’_π‘™π‘œπ‘π‘˜ . The write lock is only released upon central datastore update as a result of the follow up from edge. Thus if π‘Ÿπ‘Ÿπ‘‘π‘œ βˆ’βˆ’β†’ 𝑀 , then the read would observe a write that did not yet occur. Contradiction. ii. If and when 𝑀 is executed at the datacenter, then π‘Ÿ must have already released its read lock. In other words, π‘Ÿ and 𝑀 are executed sequentially at the datacenter, so the read would not be ordered after the write it observes. Contradiction. Thus, the ordering obeys the real-time order for all pairs of operations on each key π‘˜ . Because our given ordering is a legal total order that obeys real-time constraints, Radical provides linearizability. References [1] Maurice P Herlihy and Jeannette M Wing. Linearizability: A correctness condition for concurrent objects. ACM Transactions on Programming Languages and Systems (TOPLAS), 12(3):463–492, 1990. 3