[{"data":1,"prerenderedAt":3095},["ShallowReactive",2],{"article-en-\u002Fbackend\u002Fdistributed-systems\u002Flamport-vector-clocks":3,"article-sibling-en-\u002Fbackend\u002Fdistributed-systems\u002Flamport-vector-clocks":1653,"surround-en-\u002Fbackend\u002Fdistributed-systems\u002Flamport-vector-clocks":3062,"related-en-\u002Fbackend\u002Fdistributed-systems\u002Flamport-vector-clocks":3069},{"id":4,"title":5,"body":6,"date":1634,"description":1635,"draft":1636,"extension":1637,"img":1638,"meta":1639,"navigation":263,"path":1640,"seo":1641,"slug":1642,"stem":1643,"tags":1644,"topics":1649,"__hash__":1652},"content\u002F2.backend\u002F4.distributed-systems\u002F1.lamport-vector-clocks.md","Lamport and Vector Clocks in Go: Ordering Events Without a Shared Clock",{"type":7,"value":8,"toc":1619},"minimark",[9,28,31,44,49,73,112,115,138,173,194,201,205,208,222,450,455,469,477,493,497,511,594,607,614,618,633,637,652,655,674,863,867,892,906,912,968,1218,1221,1273,1285,1289,1296,1300,1303,1432,1442,1521,1528,1532,1562,1566,1612,1615],[10,11,12,13,17,18,21,22,24,25,27],"p",{},"In 1978, Leslie Lamport pointed out that \"before\" has little to do with time. Event ",[14,15,16],"code",{},"a"," is before event ",[14,19,20],{},"b"," only if ",[14,23,16],{}," could have influenced ",[14,26,20],{},": they are on the same machine, or a chain of messages connects them. If neither can reach the other, asking which came first has no answer.",[10,29,30],{},"That sounds like a philosophical detail until your conflict resolution depends on it. Wall clocks cannot settle the question: servers drift, NTP corrections jump them, and a 3 ms gap between two replicas is well inside the error of most setups. Even a logical clock can mislead. The classic Lamport clock gives different timestamps to nine unrelated pairs of events in ten (measured below), as if one had clearly come first.",[10,32,33,34,38,39,43],{},"This article builds the math that fixes it, an ",[35,36,37],"em",{},"order"," that allows incomparable pairs, and two algorithms that implement it, Lamport clocks and vector clocks, in Go. It is the second in a series on the math inside distributed systems, after ",[16,40,42],{"href":41},"\u002Fblog\u002Fdsa\u002Fconsistent-hashing","consistent hashing",".",[45,46,48],"h2",{"id":47},"the-math-before-is-a-partial-order","The math: \"before\" is a partial order",[10,50,51,52,54,55,57,58,60,61,63,64,68,69,72],{},"Start with what we actually want to know. For two events ",[14,53,16],{}," and ",[14,56,20],{},", we want to answer: could ",[14,59,16],{}," have influenced ",[14,62,20],{},"? Leslie Lamport defined this in 1978 as the ",[65,66,67],"strong",{},"happened-before"," relation, written ",[14,70,71],{},"a → b",", using three rules:",[74,75,76,90,100],"ol",{},[77,78,79,80,54,82,84,85,87,88,43],"li",{},"If ",[14,81,16],{},[14,83,20],{}," are on the same process and ",[14,86,16],{}," comes first, then ",[14,89,71],{},[77,91,79,92,94,95,97,98,43],{},[14,93,16],{}," sends a message and ",[14,96,20],{}," receives that same message, then ",[14,99,71],{},[77,101,79,102,54,104,107,108,111],{},[14,103,71],{},[14,105,106],{},"b → c",", then ",[14,109,110],{},"a → c"," (transitivity).",[10,113,114],{},"Nothing here mentions a clock. It only follows cause and effect: a process's own sequence of steps, and messages between processes.",[10,116,117,118,121,122,125,126,129,130,133,134,137],{},"This relation is a ",[65,119,120],{},"partial order",", and the word ",[35,123,124],{},"partial"," matters. A ",[65,127,128],{},"total order"," like ",[14,131,132],{},"1 \u003C 2 \u003C 3"," compares any two elements. A partial order allows pairs that are simply not comparable. Think of a family tree: a grandparent comes ",[35,135,136],{},"before"," their grandchildren in the line of descent, but two cousins are neither ancestor nor descendant of each other. Formally, happened-before is:",[139,140,141,147,161],"ul",{},[77,142,143,146],{},[65,144,145],{},"irreflexive",": no event happens before itself,",[77,148,149,152,153,54,155,157,158,160],{},[65,150,151],{},"transitive",": if ",[14,154,71],{},[14,156,106],{}," then ",[14,159,110],{},",",[77,162,163,166,167,54,169,172],{},[65,164,165],{},"antisymmetric",": ",[14,168,71],{},[14,170,171],{},"b → a"," can never both hold.",[10,174,175,176,178,179,181,182,185,186,189,190,193],{},"Two events with neither ",[14,177,71],{}," nor ",[14,180,171],{}," are called ",[65,183,184],{},"concurrent",", written ",[14,187,188],{},"a ∥ b",". Concurrent does not mean \"at the same instant\". It means ",[65,191,192],{},"neither could have known about the other",". Detecting that case is the whole point: those are exactly the events whose order the system must decide on purpose, or flag as a conflict.",[10,195,196,197,200],{},"So the real goal is to attach a label to every event so that comparing labels recovers ",[14,198,199],{},"→",". Lamport clocks do half of this job, vector clocks do all of it.",[45,202,204],{"id":203},"lamport-clocks-one-counter-per-process","Lamport clocks: one counter per process",[10,206,207],{},"The idea is a single integer per process. Rules:",[139,209,210,213,216],{},[77,211,212],{},"Before any local event or send, increment your counter.",[77,214,215],{},"Attach the counter to every message you send.",[77,217,218,219,43],{},"On receive, set your counter to ",[14,220,221],{},"max(yours, message's) + 1",[223,224,229],"pre",{"className":225,"code":226,"language":227,"meta":228,"style":228},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","type Lamport struct{ t uint64 }\n\n\u002F\u002F Tick: local event or send.\nfunc (c *Lamport) Tick() uint64 { c.t++; return c.t }\n\n\u002F\u002F Recv: take the max with the message's timestamp, then tick.\nfunc (c *Lamport) Recv(m uint64) uint64 {\n    if m > c.t {\n        c.t = m\n    }\n    c.t++\n    return c.t\n}\n","go","",[14,230,231,258,265,272,332,337,343,376,397,413,419,432,445],{"__ignoreMap":228},[232,233,236,240,244,247,251,255],"span",{"class":234,"line":235},"line",1,[232,237,239],{"class":238},"sMK4o","type",[232,241,243],{"class":242},"sBMFI"," Lamport",[232,245,246],{"class":238}," struct{",[232,248,250],{"class":249},"sTEyZ"," t ",[232,252,254],{"class":253},"spNyl","uint64",[232,256,257],{"class":238}," }\n",[232,259,261],{"class":234,"line":260},2,[232,262,264],{"emptyLinePlaceholder":263},true,"\n",[232,266,268],{"class":234,"line":267},3,[232,269,271],{"class":270},"sHwdD","\u002F\u002F Tick: local event or send.\n",[232,273,275,278,281,285,288,291,294,298,301,304,307,310,312,315,318,322,324,326,329],{"class":234,"line":274},4,[232,276,277],{"class":238},"func",[232,279,280],{"class":238}," (",[232,282,284],{"class":283},"sHdIc","c ",[232,286,287],{"class":238},"*",[232,289,290],{"class":242},"Lamport",[232,292,293],{"class":238},")",[232,295,297],{"class":296},"s2Zo4"," Tick",[232,299,300],{"class":238},"()",[232,302,303],{"class":253}," uint64",[232,305,306],{"class":238}," {",[232,308,309],{"class":249}," c",[232,311,43],{"class":238},[232,313,314],{"class":249},"t",[232,316,317],{"class":238},"++;",[232,319,321],{"class":320},"s7zQu"," return",[232,323,309],{"class":249},[232,325,43],{"class":238},[232,327,328],{"class":249},"t ",[232,330,331],{"class":238},"}\n",[232,333,335],{"class":234,"line":334},5,[232,336,264],{"emptyLinePlaceholder":263},[232,338,340],{"class":234,"line":339},6,[232,341,342],{"class":270},"\u002F\u002F Recv: take the max with the message's timestamp, then tick.\n",[232,344,346,348,350,352,354,356,358,361,364,367,369,371,373],{"class":234,"line":345},7,[232,347,277],{"class":238},[232,349,280],{"class":238},[232,351,284],{"class":283},[232,353,287],{"class":238},[232,355,290],{"class":242},[232,357,293],{"class":238},[232,359,360],{"class":296}," Recv",[232,362,363],{"class":238},"(",[232,365,366],{"class":283},"m",[232,368,303],{"class":253},[232,370,293],{"class":238},[232,372,303],{"class":253},[232,374,375],{"class":238}," {\n",[232,377,379,382,385,388,390,392,394],{"class":234,"line":378},8,[232,380,381],{"class":320},"    if",[232,383,384],{"class":249}," m ",[232,386,387],{"class":238},">",[232,389,309],{"class":249},[232,391,43],{"class":238},[232,393,328],{"class":249},[232,395,396],{"class":238},"{\n",[232,398,400,403,405,407,410],{"class":234,"line":399},9,[232,401,402],{"class":249},"        c",[232,404,43],{"class":238},[232,406,328],{"class":249},[232,408,409],{"class":238},"=",[232,411,412],{"class":249}," m\n",[232,414,416],{"class":234,"line":415},10,[232,417,418],{"class":238},"    }\n",[232,420,422,425,427,429],{"class":234,"line":421},11,[232,423,424],{"class":249},"    c",[232,426,43],{"class":238},[232,428,314],{"class":249},[232,430,431],{"class":238},"++\n",[232,433,435,438,440,442],{"class":234,"line":434},12,[232,436,437],{"class":320},"    return",[232,439,309],{"class":249},[232,441,43],{"class":238},[232,443,444],{"class":249},"t\n",[232,446,448],{"class":234,"line":447},13,[232,449,331],{"class":238},[451,452,454],"h3",{"id":453},"what-it-guarantees","What it guarantees",[10,456,457,458,461,462,464,465,468],{},"Write ",[14,459,460],{},"L(a)"," for the timestamp of event ",[14,463,16],{},". The guarantee, called the ",[65,466,467],{},"clock condition",", is:",[223,470,475],{"className":471,"code":473,"language":474},[472],"language-text","a → b   implies   L(a) \u003C L(b)\n","text",[14,476,473],{"__ignoreMap":228},[10,478,479,480,483,484,486,487,489,490,492],{},"The proof is short and follows the three rules. Along one process the counter only increases (rule 1). A receive sets the counter to more than the send's timestamp (rule 2). And ",[14,481,482],{},"\u003C"," on integers is transitive (rule 3). So if ",[14,485,16],{}," can influence ",[14,488,20],{},", ",[14,491,16],{}," always has the smaller number.",[451,494,496],{"id":495},"what-it-does-not-guarantee","What it does not guarantee",[10,498,499,500,503,504,507,508,510],{},"The arrow goes only one way. ",[14,501,502],{},"L(a) \u003C L(b)"," does ",[65,505,506],{},"not"," imply ",[14,509,71],{},". A concrete run with three processes shows it:",[512,513,514,533],"table",{},[515,516,517],"thead",{},[518,519,520,524,527],"tr",{},[521,522,523],"th",{},"Event",[521,525,526],{},"What happens",[521,528,529,530],{},"Lamport ",[14,531,532],{},"L",[534,535,536,548,559,570,583],"tbody",{},[518,537,538,542,545],{},[539,540,541],"td",{},"e1, e2, e3",[539,543,544],{},"A does two local events, then sends m1 to B",[539,546,547],{},"1, 2, 3",[518,549,550,553,556],{},[539,551,552],{},"e4",[539,554,555],{},"B receives m1",[539,557,558],{},"4",[518,560,561,564,567],{},[539,562,563],{},"e5",[539,565,566],{},"B sends m2 to C",[539,568,569],{},"5",[518,571,572,575,578],{},[539,573,574],{},"e6",[539,576,577],{},"C does a local event, before receiving anything",[539,579,580],{},[65,581,582],{},"1",[518,584,585,588,591],{},[539,586,587],{},"e7",[539,589,590],{},"C receives m2",[539,592,593],{},"6",[10,595,596,599,600,603,604,43],{},[14,597,598],{},"L(e6) = 1"," is smaller than ",[14,601,602],{},"L(e3) = 3",", so the numbers suggest e6 came first. But C had heard nothing from A or B when e6 happened, so the two are concurrent. Lamport clocks ",[65,605,606],{},"manufacture an order that does not exist",[10,608,609,610,613],{},"This is not a corner case. In 10,000 random executions of 4 processes (about 6.7 million event pairs), ",[65,611,612],{},"59.9% of pairs were concurrent",", and Lamport gave different timestamps to 90.0% of those concurrent pairs, making them look ordered. The clock condition itself held every time, as the proof says it must.",[451,615,617],{"id":616},"where-it-is-still-useful","Where it is still useful",[10,619,620,621,624,625,628,629,632],{},"If you only need ",[35,622,623],{},"some"," order that respects causality, Lamport is enough, and it costs one integer per message. To make it a ",[65,626,627],{},"total"," order, break ties with the process id: sort by ",[14,630,631],{},"(L, processID)",". Every node then derives the same sequence from the same events, which is the basis of simple replicated logs and distributed mutual exclusion. What you cannot do with it is ask \"were these two events concurrent?\".",[45,634,636],{"id":635},"vector-clocks-one-counter-per-process-kept-by-everyone","Vector clocks: one counter per process, kept by everyone",[10,638,639,640,643,644,647,648,651],{},"To detect concurrency we need to keep more information. Instead of one integer, each process keeps a ",[65,641,642],{},"vector",": one counter ",[35,645,646],{},"for each process",". Entry ",[14,649,650],{},"V[B]"," means \"how many of B's events I know about\".",[10,653,654],{},"Rules:",[139,656,657,664,667],{},[77,658,659,660,663],{},"On a local event or send, increment ",[65,661,662],{},"your own"," entry.",[77,665,666],{},"Attach the whole vector to every message.",[77,668,669,670,673],{},"On receive, take the ",[65,671,672],{},"component-wise maximum"," of your vector and the message's, then increment your own entry.",[223,675,677],{"className":225,"code":676,"language":227,"meta":228,"style":228},"type VClock map[string]uint64\n\nfunc (v VClock) Tick(id string) { v[id]++ }\n\nfunc (v VClock) Recv(id string, m VClock) {\n    for k, x := range m {\n        if x > v[k] {\n            v[k] = x\n        }\n    }\n    v[id]++\n}\n",[14,678,679,698,702,743,747,778,801,821,838,843,847,859],{"__ignoreMap":228},[232,680,681,683,686,689,692,695],{"class":234,"line":235},[232,682,239],{"class":238},[232,684,685],{"class":242}," VClock",[232,687,688],{"class":238}," map[",[232,690,691],{"class":253},"string",[232,693,694],{"class":238},"]",[232,696,697],{"class":253},"uint64\n",[232,699,700],{"class":234,"line":260},[232,701,264],{"emptyLinePlaceholder":263},[232,703,704,706,708,711,714,716,718,720,723,726,728,730,733,736,738,741],{"class":234,"line":267},[232,705,277],{"class":238},[232,707,280],{"class":238},[232,709,710],{"class":283},"v ",[232,712,713],{"class":242},"VClock",[232,715,293],{"class":238},[232,717,297],{"class":296},[232,719,363],{"class":238},[232,721,722],{"class":283},"id",[232,724,725],{"class":253}," string",[232,727,293],{"class":238},[232,729,306],{"class":238},[232,731,732],{"class":249}," v",[232,734,735],{"class":238},"[",[232,737,722],{"class":249},[232,739,740],{"class":238},"]++",[232,742,257],{"class":238},[232,744,745],{"class":234,"line":274},[232,746,264],{"emptyLinePlaceholder":263},[232,748,749,751,753,755,757,759,761,763,765,767,769,772,774,776],{"class":234,"line":334},[232,750,277],{"class":238},[232,752,280],{"class":238},[232,754,710],{"class":283},[232,756,713],{"class":242},[232,758,293],{"class":238},[232,760,360],{"class":296},[232,762,363],{"class":238},[232,764,722],{"class":283},[232,766,725],{"class":253},[232,768,160],{"class":238},[232,770,771],{"class":283}," m",[232,773,685],{"class":242},[232,775,293],{"class":238},[232,777,375],{"class":238},[232,779,780,783,786,788,791,794,797,799],{"class":234,"line":339},[232,781,782],{"class":320},"    for",[232,784,785],{"class":249}," k",[232,787,160],{"class":238},[232,789,790],{"class":249}," x ",[232,792,793],{"class":238},":=",[232,795,796],{"class":320}," range",[232,798,384],{"class":249},[232,800,396],{"class":238},[232,802,803,806,808,810,812,814,817,819],{"class":234,"line":345},[232,804,805],{"class":320},"        if",[232,807,790],{"class":249},[232,809,387],{"class":238},[232,811,732],{"class":249},[232,813,735],{"class":238},[232,815,816],{"class":249},"k",[232,818,694],{"class":238},[232,820,375],{"class":238},[232,822,823,826,828,830,832,835],{"class":234,"line":378},[232,824,825],{"class":249},"            v",[232,827,735],{"class":238},[232,829,816],{"class":249},[232,831,694],{"class":238},[232,833,834],{"class":238}," =",[232,836,837],{"class":249}," x\n",[232,839,840],{"class":234,"line":399},[232,841,842],{"class":238},"        }\n",[232,844,845],{"class":234,"line":415},[232,846,418],{"class":238},[232,848,849,852,854,856],{"class":234,"line":421},[232,850,851],{"class":249},"    v",[232,853,735],{"class":238},[232,855,722],{"class":249},[232,857,858],{"class":238},"]++\n",[232,860,861],{"class":234,"line":434},[232,862,331],{"class":238},[451,864,866],{"id":865},"the-math-comparing-vectors","The math: comparing vectors",[10,868,869,870,873,874,877,878,881,882,873,885,888,889,891],{},"Order two vectors ",[65,871,872],{},"component by component",". ",[14,875,876],{},"V ≤ W"," means every entry of ",[14,879,880],{},"V"," is at most the matching entry of ",[14,883,884],{},"W",[14,886,887],{},"V \u003C W"," means ",[14,890,876],{}," and they are not equal.",[10,893,894,895,54,898,901,902,905],{},"This is again a partial order, and that is exactly why it works. Two vectors can be incomparable: ",[14,896,897],{},"[A:3, B:0]",[14,899,900],{},"[A:1, B:2]"," each have one entry larger than the other, so neither is ",[14,903,904],{},"≤"," the other. The theorem:",[223,907,910],{"className":908,"code":909,"language":474},[472],"a → b    if and only if    V(a) \u003C V(b)\na ∥ b    if and only if    V(a) and V(b) are incomparable\n",[14,911,909],{"__ignoreMap":228},[10,913,914,915,918,919,922,923,926,927,930,931,933,934,936,937,939,940,942,943,945,946,949,950,952,953,955,956,958,959,961,962,964,965,967],{},"Note the ",[35,916,917],{},"if and only if",". Unlike Lamport, vector clocks preserve the order in ",[65,920,921],{},"both"," directions. The intuition is that ",[14,924,925],{},"V(a)[i]"," counts the events of process ",[14,928,929],{},"i"," that are in ",[14,932,16],{},"'s causal past. If ",[14,935,16],{}," is in ",[14,938,20],{},"'s past, then everything in ",[14,941,16],{},"'s past is also in ",[14,944,20],{},"'s past, so every entry of ",[14,947,948],{},"V(b)"," is at least as large, and ",[14,951,20],{}," itself adds one more. If ",[14,954,16],{}," is not in ",[14,957,20],{},"'s past, ",[14,960,20],{}," has not seen at least one event that ",[14,963,16],{}," has, and some entry of ",[14,966,948],{}," is smaller. Comparison is then a few lines:",[223,969,971],{"className":225,"code":970,"language":227,"meta":228,"style":228},"func Compare(a, b VClock) Order {\n    aLE, bLE := true, true \u002F\u002F a \u003C= b ?   b \u003C= a ?\n    for k, x := range a {\n        if x > b[k] {\n            aLE = false\n        }\n    }\n    for k, x := range b {\n        if x > a[k] {\n            bLE = false\n        }\n    }\n    switch {\n    case aLE && bLE:\n        return Equal\n    case aLE:\n        return Before\n    case bLE:\n        return After\n    }\n    return Concurrent\n}\n",[14,972,973,998,1021,1040,1058,1068,1072,1076,1095,1114,1123,1127,1131,1138,1156,1165,1175,1183,1192,1200,1205,1213],{"__ignoreMap":228},[232,974,975,977,980,982,984,986,989,991,993,996],{"class":234,"line":235},[232,976,277],{"class":238},[232,978,979],{"class":296}," Compare",[232,981,363],{"class":238},[232,983,16],{"class":283},[232,985,160],{"class":238},[232,987,988],{"class":283}," b",[232,990,685],{"class":242},[232,992,293],{"class":238},[232,994,995],{"class":242}," Order",[232,997,375],{"class":238},[232,999,1000,1003,1005,1008,1010,1014,1016,1018],{"class":234,"line":260},[232,1001,1002],{"class":249},"    aLE",[232,1004,160],{"class":238},[232,1006,1007],{"class":249}," bLE ",[232,1009,793],{"class":238},[232,1011,1013],{"class":1012},"sfNiH"," true",[232,1015,160],{"class":238},[232,1017,1013],{"class":1012},[232,1019,1020],{"class":270}," \u002F\u002F a \u003C= b ?   b \u003C= a ?\n",[232,1022,1023,1025,1027,1029,1031,1033,1035,1038],{"class":234,"line":267},[232,1024,782],{"class":320},[232,1026,785],{"class":249},[232,1028,160],{"class":238},[232,1030,790],{"class":249},[232,1032,793],{"class":238},[232,1034,796],{"class":320},[232,1036,1037],{"class":249}," a ",[232,1039,396],{"class":238},[232,1041,1042,1044,1046,1048,1050,1052,1054,1056],{"class":234,"line":274},[232,1043,805],{"class":320},[232,1045,790],{"class":249},[232,1047,387],{"class":238},[232,1049,988],{"class":249},[232,1051,735],{"class":238},[232,1053,816],{"class":249},[232,1055,694],{"class":238},[232,1057,375],{"class":238},[232,1059,1060,1063,1065],{"class":234,"line":334},[232,1061,1062],{"class":249},"            aLE ",[232,1064,409],{"class":238},[232,1066,1067],{"class":1012}," false\n",[232,1069,1070],{"class":234,"line":339},[232,1071,842],{"class":238},[232,1073,1074],{"class":234,"line":345},[232,1075,418],{"class":238},[232,1077,1078,1080,1082,1084,1086,1088,1090,1093],{"class":234,"line":378},[232,1079,782],{"class":320},[232,1081,785],{"class":249},[232,1083,160],{"class":238},[232,1085,790],{"class":249},[232,1087,793],{"class":238},[232,1089,796],{"class":320},[232,1091,1092],{"class":249}," b ",[232,1094,396],{"class":238},[232,1096,1097,1099,1101,1103,1106,1108,1110,1112],{"class":234,"line":399},[232,1098,805],{"class":320},[232,1100,790],{"class":249},[232,1102,387],{"class":238},[232,1104,1105],{"class":249}," a",[232,1107,735],{"class":238},[232,1109,816],{"class":249},[232,1111,694],{"class":238},[232,1113,375],{"class":238},[232,1115,1116,1119,1121],{"class":234,"line":415},[232,1117,1118],{"class":249},"            bLE ",[232,1120,409],{"class":238},[232,1122,1067],{"class":1012},[232,1124,1125],{"class":234,"line":421},[232,1126,842],{"class":238},[232,1128,1129],{"class":234,"line":434},[232,1130,418],{"class":238},[232,1132,1133,1136],{"class":234,"line":447},[232,1134,1135],{"class":320},"    switch",[232,1137,375],{"class":238},[232,1139,1141,1144,1147,1150,1153],{"class":234,"line":1140},14,[232,1142,1143],{"class":320},"    case",[232,1145,1146],{"class":249}," aLE ",[232,1148,1149],{"class":238},"&&",[232,1151,1152],{"class":249}," bLE",[232,1154,1155],{"class":238},":\n",[232,1157,1159,1162],{"class":234,"line":1158},15,[232,1160,1161],{"class":320},"        return",[232,1163,1164],{"class":249}," Equal\n",[232,1166,1168,1170,1173],{"class":234,"line":1167},16,[232,1169,1143],{"class":320},[232,1171,1172],{"class":249}," aLE",[232,1174,1155],{"class":238},[232,1176,1178,1180],{"class":234,"line":1177},17,[232,1179,1161],{"class":320},[232,1181,1182],{"class":249}," Before\n",[232,1184,1186,1188,1190],{"class":234,"line":1185},18,[232,1187,1143],{"class":320},[232,1189,1152],{"class":249},[232,1191,1155],{"class":238},[232,1193,1195,1197],{"class":234,"line":1194},19,[232,1196,1161],{"class":320},[232,1198,1199],{"class":249}," After\n",[232,1201,1203],{"class":234,"line":1202},20,[232,1204,418],{"class":238},[232,1206,1208,1210],{"class":234,"line":1207},21,[232,1209,437],{"class":320},[232,1211,1212],{"class":249}," Concurrent\n",[232,1214,1216],{"class":234,"line":1215},22,[232,1217,331],{"class":238},[10,1219,1220],{},"On the same run as above, the two methods differ on exactly the pair that matters:",[512,1222,1223,1234],{},[515,1224,1225],{},[518,1226,1227,1229,1231],{},[521,1228,523],{},[521,1230,290],{},[521,1232,1233],{},"Vector clock",[534,1235,1236,1249,1261],{},[518,1237,1238,1241,1244],{},[539,1239,1240],{},"e3 (A sends m1)",[539,1242,1243],{},"3",[539,1245,1246],{},[14,1247,1248],{},"[A:3]",[518,1250,1251,1254,1256],{},[539,1252,1253],{},"e6 (C, local)",[539,1255,582],{},[539,1257,1258],{},[14,1259,1260],{},"[C:1]",[518,1262,1263,1266,1268],{},[539,1264,1265],{},"e7 (C receives m2)",[539,1267,593],{},[539,1269,1270],{},[14,1271,1272],{},"[A:3 B:2 C:2]",[10,1274,1275,1276,1278,1279,1281,1282,1284],{},"Comparing e3 and e6 gives ",[65,1277,184],{}," (neither vector is ",[14,1280,904],{}," the other), while Lamport said e6 came first. Comparing e3 and e7 gives ",[65,1283,136],{},", which is correct: e3 → e4 → e5 → e7 through the messages.",[451,1286,1288],{"id":1287},"checking-the-claim","Checking the claim",[10,1290,1291,1292,1295],{},"I did not want to rely on the proof alone. I generated 10,000 random executions (4 processes, 40 steps, random sends and receives), computed the true happened-before relation by transitive closure, and compared. Result over all 6.7 million pairs: the vector clock verdict matched the true relation ",[65,1293,1294],{},"every single time",", concurrent pairs included.",[45,1297,1299],{"id":1298},"using-it-detecting-a-write-conflict","Using it: detecting a write conflict",[10,1301,1302],{},"This is the use case that made vector clocks well known: Amazon's Dynamo, and databases in its lineage such as Riak. Two replicas accept writes to the same cart starting from the same version:",[223,1304,1306],{"className":225,"code":1305,"language":227,"meta":228,"style":228},"base := VClock{\"A\": 1}\nwa := base.Copy(); wa.Tick(\"A\")   \u002F\u002F replica A writes: [A:2]\nwb := base.Copy(); wb.Tick(\"B\")   \u002F\u002F replica B writes: [A:1 B:1]\nCompare(wa, wb)                   \u002F\u002F Concurrent\n",[14,1307,1308,1338,1377,1413],{"__ignoreMap":228},[232,1309,1310,1313,1315,1317,1320,1323,1327,1329,1332,1336],{"class":234,"line":235},[232,1311,1312],{"class":249},"base ",[232,1314,793],{"class":238},[232,1316,685],{"class":242},[232,1318,1319],{"class":238},"{",[232,1321,1322],{"class":238},"\"",[232,1324,1326],{"class":1325},"sfazB","A",[232,1328,1322],{"class":238},[232,1330,1331],{"class":238},":",[232,1333,1335],{"class":1334},"sbssI"," 1",[232,1337,331],{"class":238},[232,1339,1340,1343,1345,1348,1350,1353,1356,1359,1361,1364,1366,1368,1370,1372,1374],{"class":234,"line":260},[232,1341,1342],{"class":249},"wa ",[232,1344,793],{"class":238},[232,1346,1347],{"class":249}," base",[232,1349,43],{"class":238},[232,1351,1352],{"class":296},"Copy",[232,1354,1355],{"class":238},"();",[232,1357,1358],{"class":249}," wa",[232,1360,43],{"class":238},[232,1362,1363],{"class":296},"Tick",[232,1365,363],{"class":238},[232,1367,1322],{"class":238},[232,1369,1326],{"class":1325},[232,1371,1322],{"class":238},[232,1373,293],{"class":238},[232,1375,1376],{"class":270},"   \u002F\u002F replica A writes: [A:2]\n",[232,1378,1379,1382,1384,1386,1388,1390,1392,1395,1397,1399,1401,1403,1406,1408,1410],{"class":234,"line":267},[232,1380,1381],{"class":249},"wb ",[232,1383,793],{"class":238},[232,1385,1347],{"class":249},[232,1387,43],{"class":238},[232,1389,1352],{"class":296},[232,1391,1355],{"class":238},[232,1393,1394],{"class":249}," wb",[232,1396,43],{"class":238},[232,1398,1363],{"class":296},[232,1400,363],{"class":238},[232,1402,1322],{"class":238},[232,1404,1405],{"class":1325},"B",[232,1407,1322],{"class":238},[232,1409,293],{"class":238},[232,1411,1412],{"class":270},"   \u002F\u002F replica B writes: [A:1 B:1]\n",[232,1414,1415,1418,1420,1423,1425,1427,1429],{"class":234,"line":274},[232,1416,1417],{"class":296},"Compare",[232,1419,363],{"class":238},[232,1421,1422],{"class":249},"wa",[232,1424,160],{"class":238},[232,1426,1394],{"class":249},[232,1428,293],{"class":238},[232,1430,1431],{"class":270},"                   \u002F\u002F Concurrent\n",[10,1433,1434,1435,1437,1438,1441],{},"Neither version is ",[14,1436,904],{}," the other, so the system knows it holds ",[65,1439,1440],{},"two siblings",", not a newer and an older version. A newer-wins rule on wall-clock time would have deleted one without telling anyone. Here the database can hand both to the application to merge (take the union of the cart items, for example). The client then writes the merge with a clock that dominates both:",[223,1443,1445],{"className":225,"code":1444,"language":227,"meta":228,"style":228},"merged := wa.Copy(); merged.Recv(\"A\", wb)  \u002F\u002F [A:3 B:1]\nCompare(wa, merged)  \u002F\u002F Before\nCompare(wb, merged)  \u002F\u002F Before\n",[14,1446,1447,1487,1504],{"__ignoreMap":228},[232,1448,1449,1452,1454,1456,1458,1460,1462,1465,1467,1470,1472,1474,1476,1478,1480,1482,1484],{"class":234,"line":235},[232,1450,1451],{"class":249},"merged ",[232,1453,793],{"class":238},[232,1455,1358],{"class":249},[232,1457,43],{"class":238},[232,1459,1352],{"class":296},[232,1461,1355],{"class":238},[232,1463,1464],{"class":249}," merged",[232,1466,43],{"class":238},[232,1468,1469],{"class":296},"Recv",[232,1471,363],{"class":238},[232,1473,1322],{"class":238},[232,1475,1326],{"class":1325},[232,1477,1322],{"class":238},[232,1479,160],{"class":238},[232,1481,1394],{"class":249},[232,1483,293],{"class":238},[232,1485,1486],{"class":270},"  \u002F\u002F [A:3 B:1]\n",[232,1488,1489,1491,1493,1495,1497,1499,1501],{"class":234,"line":260},[232,1490,1417],{"class":296},[232,1492,363],{"class":238},[232,1494,1422],{"class":249},[232,1496,160],{"class":238},[232,1498,1464],{"class":249},[232,1500,293],{"class":238},[232,1502,1503],{"class":270},"  \u002F\u002F Before\n",[232,1505,1506,1508,1510,1513,1515,1517,1519],{"class":234,"line":267},[232,1507,1417],{"class":296},[232,1509,363],{"class":238},[232,1511,1512],{"class":249},"wb",[232,1514,160],{"class":238},[232,1516,1464],{"class":249},[232,1518,293],{"class":238},[232,1520,1503],{"class":270},[10,1522,1523,1524,1527],{},"The merged version is strictly greater than both siblings, so the conflict is resolved and both old versions can be discarded. Keep this picture in mind. The operation \"take the component-wise maximum\" is a ",[65,1525,1526],{},"join",", the smallest vector that is above both inputs. It is associative, commutative and idempotent, and the next article in the series, on CRDTs, is built on exactly that property.",[45,1529,1531],{"id":1530},"costs-and-alternatives","Costs and alternatives",[139,1533,1534,1544,1550,1556],{},[77,1535,1536,1539,1540,1543],{},[65,1537,1538],{},"Size grows with the number of processes."," A vector has one entry per writer, so every message and every stored value carries ",[14,1541,1542],{},"O(n)"," extra data. Systems with many short-lived clients need pruning or smarter variants such as dotted version vectors.",[77,1545,1546,1549],{},[65,1547,1548],{},"Concurrent writes still need a decision."," Detecting a conflict is not resolving it. Dynamo-style systems hand siblings to the application, others pick a rule such as last-writer-wins and accept losing data.",[77,1551,1552,1555],{},[65,1553,1554],{},"Hybrid logical clocks (HLC)"," combine a physical timestamp with a Lamport-style counter. They keep a constant size and stay close to real time, which is why systems such as CockroachDB use them. Like Lamport clocks, they preserve causality but cannot detect concurrency.",[77,1557,1558,1561],{},[65,1559,1560],{},"Physical clocks can be enough if you pay for it."," Google's Spanner bounds clock uncertainty with special hardware and waits it out. That is an engineering answer where this article gave a mathematical one.",[45,1563,1565],{"id":1564},"takeaways","Takeaways",[139,1567,1568,1574,1581,1593,1609],{},[77,1569,1570,1571,1573],{},"Wall clocks cannot order events across machines. What you want is the ",[65,1572,67],{}," relation, a partial order built from local sequence and messages.",[77,1575,1576,1577,1580],{},"In a partial order some pairs are ",[65,1578,1579],{},"incomparable",". Those are the concurrent events, and they are where conflicts live.",[77,1582,1583,1586,1587,1589,1590,1592],{},[65,1584,1585],{},"Lamport clocks"," keep one counter: ",[14,1588,71],{}," implies ",[14,1591,502],{},", but not the reverse. In my test they made 90% of concurrent pairs look ordered. Use them when any consistent order is enough.",[77,1594,1595,1598,1599,1601,1602,1605,1606,1608],{},[65,1596,1597],{},"Vector clocks"," keep one counter per process and compare component-wise: ",[14,1600,71],{}," if and only if ",[14,1603,1604],{},"V(a) \u003C V(b)",". They detect concurrency exactly, at ",[14,1607,1542],{}," cost per message.",[77,1610,1611],{},"The component-wise maximum used to merge clocks is a join, the same operation that powers CRDTs.",[10,1613,1614],{},"Next in the series: CRDTs, and how the join operation lets replicas merge without conflicts.",[1616,1617,1618],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":228,"searchDepth":260,"depth":260,"links":1620},[1621,1622,1627,1631,1632,1633],{"id":47,"depth":260,"text":48},{"id":203,"depth":260,"text":204,"children":1623},[1624,1625,1626],{"id":453,"depth":267,"text":454},{"id":495,"depth":267,"text":496},{"id":616,"depth":267,"text":617},{"id":635,"depth":260,"text":636,"children":1628},[1629,1630],{"id":865,"depth":267,"text":866},{"id":1287,"depth":267,"text":1288},{"id":1298,"depth":260,"text":1299},{"id":1530,"depth":260,"text":1531},{"id":1564,"depth":260,"text":1565},"2026-11-14","Two servers write the same key and your logs say one came 3 ms before the other. Can you trust that? Not without a shared clock. We build Lamport and vector clocks in Go, explain the small amount of math behind them (partial orders, the happened-before relation, component-wise comparison), and test both against 6.7 million event pairs to see exactly what each one can and cannot tell you.",false,"md","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Fv1691560968\u002FBlog\u002Farticles\u002Falgorithms\u002Fbolohyzwbz34pedr1zpp.png",{},"\u002Fbackend\u002Fdistributed-systems\u002Flamport-vector-clocks",{"title":5,"description":1635},"lamport-vector-clocks-go","2.backend\u002F4.distributed-systems\u002F1.lamport-vector-clocks",[1645,1646,1647,1648],"Distributed-Systems","Go","Logical-Clocks","Algorithms",[1650,1651],"backend","dsa","TCD3pMAJo0W90s_baOUxYwfbGxTCdaW-8XnId2l-3GQ",{"id":1654,"title":1655,"body":1656,"date":1634,"description":3053,"draft":1636,"extension":1637,"img":1638,"meta":3054,"navigation":263,"path":3055,"seo":3056,"slug":3057,"stem":3058,"tags":3059,"topics":3060,"__hash__":3061},"content\u002F2.backend\u002F4.distributed-systems\u002F1.lamport-vector-clocks.fr.md","Horloges de Lamport et vectorielles en Go : ordonner des événements sans horloge partagée",{"type":7,"value":1657,"toc":3038},[1658,1673,1676,1685,1689,1710,1745,1748,1770,1801,1821,1827,1831,1834,1848,2009,2013,2026,2032,2046,2050,2063,2128,2139,2146,2150,2163,2167,2181,2184,2203,2365,2369,2389,2400,2406,2458,2672,2675,2722,2734,2738,2745,2749,2752,2861,2871,2943,2950,2954,2983,2987,3033,3036],[10,1659,1660,1661,1663,1664,1666,1667,1669,1670,1672],{},"En 1978, Leslie Lamport a fait remarquer que « avant » n'a presque rien à voir avec le temps. L'événement ",[14,1662,16],{}," est avant l'événement ",[14,1665,20],{}," seulement si ",[14,1668,16],{}," a pu influencer ",[14,1671,20],{}," : ils sont sur la même machine, ou une chaîne de messages les relie. Si aucun des deux ne peut atteindre l'autre, demander lequel est venu en premier n'a pas de réponse.",[10,1674,1675],{},"Cela ressemble à un détail philosophique, jusqu'à ce que votre résolution de conflit en dépende. Les horloges murales ne tranchent pas : les serveurs dérivent, les corrections NTP les font sauter, et un écart de 3 ms entre deux réplicas est largement dans la marge d'erreur de la plupart des installations. Même une horloge logique peut tromper. L'horloge de Lamport classique donne des horodatages différents à neuf paires d'événements sans lien sur dix (mesuré plus bas), comme si l'un était clairement venu avant.",[10,1677,1678,1679,1682,1683,43],{},"Cet article construit les maths qui corrigent cela, un ",[35,1680,1681],{},"ordre"," qui admet des paires incomparables, et deux algorithmes qui l'implémentent, les horloges de Lamport et les horloges vectorielles, en Go. C'est le deuxième d'une série sur les maths au cœur des systèmes distribués, après le ",[16,1684,42],{"href":41},[45,1686,1688],{"id":1687},"les-maths-avant-est-un-ordre-partiel","Les maths : « avant » est un ordre partiel",[10,1690,1691,1692,1694,1695,1697,1698,1700,1701,1703,1704,1706,1707,1709],{},"Partons de ce qu'on veut vraiment savoir. Pour deux événements ",[14,1693,16],{}," et ",[14,1696,20],{},", on veut répondre : ",[14,1699,16],{}," a-t-il pu influencer ",[14,1702,20],{}," ? Leslie Lamport a défini cela en 1978 comme la relation ",[65,1705,67],{}," (« s'est produit avant »), notée ",[14,1708,71],{},", avec trois règles :",[74,1711,1712,1725,1735],{},[77,1713,1714,1715,1694,1717,1719,1720,1722,1723,43],{},"Si ",[14,1716,16],{},[14,1718,20],{}," sont sur le même processus et que ",[14,1721,16],{}," vient en premier, alors ",[14,1724,71],{},[77,1726,1714,1727,1729,1730,1732,1733,43],{},[14,1728,16],{}," envoie un message et que ",[14,1731,20],{}," reçoit ce même message, alors ",[14,1734,71],{},[77,1736,1714,1737,1694,1739,1741,1742,1744],{},[14,1738,71],{},[14,1740,106],{},", alors ",[14,1743,110],{}," (transitivité).",[10,1746,1747],{},"Rien ici ne mentionne une horloge. On suit seulement la cause et l'effet : la suite d'étapes d'un processus, et les messages entre processus.",[10,1749,1750,1751,1754,1755,1758,1759,1762,1763,1765,1766,1769],{},"Cette relation est un ",[65,1752,1753],{},"ordre partiel",", et le mot ",[35,1756,1757],{},"partiel"," compte. Un ",[65,1760,1761],{},"ordre total"," comme ",[14,1764,132],{}," compare deux éléments quelconques. Un ordre partiel admet des paires simplement non comparables. Pensez à un arbre généalogique : un grand-parent vient ",[35,1767,1768],{},"avant"," ses petits-enfants dans la lignée, mais deux cousins ne sont ni ancêtre ni descendant l'un de l'autre. Formellement, happened-before est :",[139,1771,1772,1778,1790],{},[77,1773,1774,1777],{},[65,1775,1776],{},"irréflexive"," : aucun événement ne se produit avant lui-même,",[77,1779,1780,1782,1783,1694,1785,1787,1788,160],{},[65,1781,151],{}," : si ",[14,1784,71],{},[14,1786,106],{}," alors ",[14,1789,110],{},[77,1791,1792,1795,1796,1694,1798,1800],{},[65,1793,1794],{},"antisymétrique"," : ",[14,1797,71],{},[14,1799,171],{}," ne peuvent jamais être vrais ensemble.",[10,1802,1803,1804,1806,1807,1809,1810,1813,1814,1816,1817,1820],{},"Deux événements sans ",[14,1805,71],{}," ni ",[14,1808,171],{}," sont dits ",[65,1811,1812],{},"concurrents",", notés ",[14,1815,188],{},". Concurrent ne veut pas dire « au même instant ». Cela veut dire que ",[65,1818,1819],{},"ni l'un ni l'autre n'a pu connaître l'autre",". Détecter ce cas est tout l'enjeu : ce sont exactement les événements dont le système doit décider l'ordre exprès, ou signaler comme conflit.",[10,1822,1823,1824,1826],{},"Le vrai objectif est donc d'attacher une étiquette à chaque événement pour que comparer les étiquettes retrouve ",[14,1825,199],{},". Les horloges de Lamport font la moitié du travail, les horloges vectorielles le font entièrement.",[45,1828,1830],{"id":1829},"horloges-de-lamport-un-compteur-par-processus","Horloges de Lamport : un compteur par processus",[10,1832,1833],{},"L'idée est un seul entier par processus. Règles :",[139,1835,1836,1839,1842],{},[77,1837,1838],{},"Avant tout événement local ou envoi, incrémentez votre compteur.",[77,1840,1841],{},"Joignez le compteur à chaque message envoyé.",[77,1843,1844,1845,43],{},"À la réception, mettez votre compteur à ",[14,1846,1847],{},"max(le vôtre, celui du message) + 1",[223,1849,1851],{"className":225,"code":1850,"language":227,"meta":228,"style":228},"type Lamport struct{ t uint64 }\n\n\u002F\u002F Tick : événement local ou envoi.\nfunc (c *Lamport) Tick() uint64 { c.t++; return c.t }\n\n\u002F\u002F Recv : max avec l'horodatage du message, puis tick.\nfunc (c *Lamport) Recv(m uint64) uint64 {\n    if m > c.t {\n        c.t = m\n    }\n    c.t++\n    return c.t\n}\n",[14,1852,1853,1867,1871,1876,1916,1920,1925,1953,1969,1981,1985,1995,2005],{"__ignoreMap":228},[232,1854,1855,1857,1859,1861,1863,1865],{"class":234,"line":235},[232,1856,239],{"class":238},[232,1858,243],{"class":242},[232,1860,246],{"class":238},[232,1862,250],{"class":249},[232,1864,254],{"class":253},[232,1866,257],{"class":238},[232,1868,1869],{"class":234,"line":260},[232,1870,264],{"emptyLinePlaceholder":263},[232,1872,1873],{"class":234,"line":267},[232,1874,1875],{"class":270},"\u002F\u002F Tick : événement local ou envoi.\n",[232,1877,1878,1880,1882,1884,1886,1888,1890,1892,1894,1896,1898,1900,1902,1904,1906,1908,1910,1912,1914],{"class":234,"line":274},[232,1879,277],{"class":238},[232,1881,280],{"class":238},[232,1883,284],{"class":283},[232,1885,287],{"class":238},[232,1887,290],{"class":242},[232,1889,293],{"class":238},[232,1891,297],{"class":296},[232,1893,300],{"class":238},[232,1895,303],{"class":253},[232,1897,306],{"class":238},[232,1899,309],{"class":249},[232,1901,43],{"class":238},[232,1903,314],{"class":249},[232,1905,317],{"class":238},[232,1907,321],{"class":320},[232,1909,309],{"class":249},[232,1911,43],{"class":238},[232,1913,328],{"class":249},[232,1915,331],{"class":238},[232,1917,1918],{"class":234,"line":334},[232,1919,264],{"emptyLinePlaceholder":263},[232,1921,1922],{"class":234,"line":339},[232,1923,1924],{"class":270},"\u002F\u002F Recv : max avec l'horodatage du message, puis tick.\n",[232,1926,1927,1929,1931,1933,1935,1937,1939,1941,1943,1945,1947,1949,1951],{"class":234,"line":345},[232,1928,277],{"class":238},[232,1930,280],{"class":238},[232,1932,284],{"class":283},[232,1934,287],{"class":238},[232,1936,290],{"class":242},[232,1938,293],{"class":238},[232,1940,360],{"class":296},[232,1942,363],{"class":238},[232,1944,366],{"class":283},[232,1946,303],{"class":253},[232,1948,293],{"class":238},[232,1950,303],{"class":253},[232,1952,375],{"class":238},[232,1954,1955,1957,1959,1961,1963,1965,1967],{"class":234,"line":378},[232,1956,381],{"class":320},[232,1958,384],{"class":249},[232,1960,387],{"class":238},[232,1962,309],{"class":249},[232,1964,43],{"class":238},[232,1966,328],{"class":249},[232,1968,396],{"class":238},[232,1970,1971,1973,1975,1977,1979],{"class":234,"line":399},[232,1972,402],{"class":249},[232,1974,43],{"class":238},[232,1976,328],{"class":249},[232,1978,409],{"class":238},[232,1980,412],{"class":249},[232,1982,1983],{"class":234,"line":415},[232,1984,418],{"class":238},[232,1986,1987,1989,1991,1993],{"class":234,"line":421},[232,1988,424],{"class":249},[232,1990,43],{"class":238},[232,1992,314],{"class":249},[232,1994,431],{"class":238},[232,1996,1997,1999,2001,2003],{"class":234,"line":434},[232,1998,437],{"class":320},[232,2000,309],{"class":249},[232,2002,43],{"class":238},[232,2004,444],{"class":249},[232,2006,2007],{"class":234,"line":447},[232,2008,331],{"class":238},[451,2010,2012],{"id":2011},"ce-que-ça-garantit","Ce que ça garantit",[10,2014,2015,2016,2018,2019,2021,2022,2025],{},"Notons ",[14,2017,460],{}," l'horodatage de l'événement ",[14,2020,16],{},". La garantie, appelée ",[65,2023,2024],{},"condition d'horloge",", est :",[223,2027,2030],{"className":2028,"code":2029,"language":474},[472],"a → b   implique   L(a) \u003C L(b)\n",[14,2031,2029],{"__ignoreMap":228},[10,2033,2034,2035,2037,2038,2040,2041,489,2043,2045],{},"La preuve est courte et suit les trois règles. Sur un même processus le compteur ne fait qu'augmenter (règle 1). Une réception met le compteur au-dessus de l'horodatage de l'envoi (règle 2). Et ",[14,2036,482],{}," sur les entiers est transitif (règle 3). Donc si ",[14,2039,16],{}," peut influencer ",[14,2042,20],{},[14,2044,16],{}," a toujours le plus petit numéro.",[451,2047,2049],{"id":2048},"ce-que-ça-ne-garantit-pas","Ce que ça ne garantit pas",[10,2051,2052,2053,2055,2056,2059,2060,2062],{},"La flèche ne va que dans un sens. ",[14,2054,502],{}," n'implique ",[65,2057,2058],{},"pas"," ",[14,2061,71],{},". Une exécution concrète à trois processus le montre :",[512,2064,2065,2079],{},[515,2066,2067],{},[518,2068,2069,2072,2075],{},[521,2070,2071],{},"Événement",[521,2073,2074],{},"Ce qui se passe",[521,2076,529,2077],{},[14,2078,532],{},[534,2080,2081,2090,2099,2108,2119],{},[518,2082,2083,2085,2088],{},[539,2084,541],{},[539,2086,2087],{},"A fait deux événements locaux, puis envoie m1 à B",[539,2089,547],{},[518,2091,2092,2094,2097],{},[539,2093,552],{},[539,2095,2096],{},"B reçoit m1",[539,2098,558],{},[518,2100,2101,2103,2106],{},[539,2102,563],{},[539,2104,2105],{},"B envoie m2 à C",[539,2107,569],{},[518,2109,2110,2112,2115],{},[539,2111,574],{},[539,2113,2114],{},"C fait un événement local, avant d'avoir rien reçu",[539,2116,2117],{},[65,2118,582],{},[518,2120,2121,2123,2126],{},[539,2122,587],{},[539,2124,2125],{},"C reçoit m2",[539,2127,593],{},[10,2129,2130,2132,2133,2135,2136,43],{},[14,2131,598],{}," est plus petit que ",[14,2134,602],{},", donc les nombres suggèrent que e6 est venu avant. Mais C n'avait rien reçu de A ni de B quand e6 s'est produit : les deux sont concurrents. Les horloges de Lamport ",[65,2137,2138],{},"fabriquent un ordre qui n'existe pas",[10,2140,2141,2142,2145],{},"Ce n'est pas un cas tordu. Sur 10 000 exécutions aléatoires de 4 processus (environ 6,7 millions de paires d'événements), ",[65,2143,2144],{},"59,9 % des paires étaient concurrentes",", et Lamport a donné des horodatages différents à 90,0 % de ces paires concurrentes, qui paraissent donc ordonnées. La condition d'horloge elle-même a tenu à chaque fois, comme la preuve le dit.",[451,2147,2149],{"id":2148},"où-cest-quand-même-utile","Où c'est quand même utile",[10,2151,2152,2153,2156,2157,2159,2160,2162],{},"Si vous avez seulement besoin d'",[35,2154,2155],{},"un"," ordre qui respecte la causalité, Lamport suffit, pour le prix d'un entier par message. Pour en faire un ordre ",[65,2158,627],{},", départagez les égalités avec l'id du processus : triez par ",[14,2161,631],{},". Chaque nœud dérive alors la même séquence des mêmes événements, ce qui fonde les logs répliqués simples et l'exclusion mutuelle distribuée. Ce que vous ne pouvez pas faire, c'est demander « ces deux événements étaient-ils concurrents ? ».",[45,2164,2166],{"id":2165},"horloges-vectorielles-un-compteur-par-processus-gardé-par-tous","Horloges vectorielles : un compteur par processus, gardé par tous",[10,2168,2169,2170,2173,2174,2177,2178,2180],{},"Pour détecter la concurrence il faut garder plus d'information. Au lieu d'un entier, chaque processus garde un ",[65,2171,2172],{},"vecteur"," : un compteur ",[35,2175,2176],{},"pour chaque processus",". L'entrée ",[14,2179,650],{}," veut dire « combien d'événements de B je connais ».",[10,2182,2183],{},"Règles :",[139,2185,2186,2193,2196],{},[77,2187,2188,2189,2192],{},"Sur un événement local ou un envoi, incrémentez ",[65,2190,2191],{},"votre propre"," entrée.",[77,2194,2195],{},"Joignez le vecteur entier à chaque message.",[77,2197,2198,2199,2202],{},"À la réception, prenez le ",[65,2200,2201],{},"maximum composante par composante"," de votre vecteur et de celui du message, puis incrémentez votre propre entrée.",[223,2204,2205],{"className":225,"code":676,"language":227,"meta":228,"style":228},[14,2206,2207,2221,2225,2259,2263,2293,2311,2329,2343,2347,2351,2361],{"__ignoreMap":228},[232,2208,2209,2211,2213,2215,2217,2219],{"class":234,"line":235},[232,2210,239],{"class":238},[232,2212,685],{"class":242},[232,2214,688],{"class":238},[232,2216,691],{"class":253},[232,2218,694],{"class":238},[232,2220,697],{"class":253},[232,2222,2223],{"class":234,"line":260},[232,2224,264],{"emptyLinePlaceholder":263},[232,2226,2227,2229,2231,2233,2235,2237,2239,2241,2243,2245,2247,2249,2251,2253,2255,2257],{"class":234,"line":267},[232,2228,277],{"class":238},[232,2230,280],{"class":238},[232,2232,710],{"class":283},[232,2234,713],{"class":242},[232,2236,293],{"class":238},[232,2238,297],{"class":296},[232,2240,363],{"class":238},[232,2242,722],{"class":283},[232,2244,725],{"class":253},[232,2246,293],{"class":238},[232,2248,306],{"class":238},[232,2250,732],{"class":249},[232,2252,735],{"class":238},[232,2254,722],{"class":249},[232,2256,740],{"class":238},[232,2258,257],{"class":238},[232,2260,2261],{"class":234,"line":274},[232,2262,264],{"emptyLinePlaceholder":263},[232,2264,2265,2267,2269,2271,2273,2275,2277,2279,2281,2283,2285,2287,2289,2291],{"class":234,"line":334},[232,2266,277],{"class":238},[232,2268,280],{"class":238},[232,2270,710],{"class":283},[232,2272,713],{"class":242},[232,2274,293],{"class":238},[232,2276,360],{"class":296},[232,2278,363],{"class":238},[232,2280,722],{"class":283},[232,2282,725],{"class":253},[232,2284,160],{"class":238},[232,2286,771],{"class":283},[232,2288,685],{"class":242},[232,2290,293],{"class":238},[232,2292,375],{"class":238},[232,2294,2295,2297,2299,2301,2303,2305,2307,2309],{"class":234,"line":339},[232,2296,782],{"class":320},[232,2298,785],{"class":249},[232,2300,160],{"class":238},[232,2302,790],{"class":249},[232,2304,793],{"class":238},[232,2306,796],{"class":320},[232,2308,384],{"class":249},[232,2310,396],{"class":238},[232,2312,2313,2315,2317,2319,2321,2323,2325,2327],{"class":234,"line":345},[232,2314,805],{"class":320},[232,2316,790],{"class":249},[232,2318,387],{"class":238},[232,2320,732],{"class":249},[232,2322,735],{"class":238},[232,2324,816],{"class":249},[232,2326,694],{"class":238},[232,2328,375],{"class":238},[232,2330,2331,2333,2335,2337,2339,2341],{"class":234,"line":378},[232,2332,825],{"class":249},[232,2334,735],{"class":238},[232,2336,816],{"class":249},[232,2338,694],{"class":238},[232,2340,834],{"class":238},[232,2342,837],{"class":249},[232,2344,2345],{"class":234,"line":399},[232,2346,842],{"class":238},[232,2348,2349],{"class":234,"line":415},[232,2350,418],{"class":238},[232,2352,2353,2355,2357,2359],{"class":234,"line":421},[232,2354,851],{"class":249},[232,2356,735],{"class":238},[232,2358,722],{"class":249},[232,2360,858],{"class":238},[232,2362,2363],{"class":234,"line":434},[232,2364,331],{"class":238},[451,2366,2368],{"id":2367},"les-maths-comparer-des-vecteurs","Les maths : comparer des vecteurs",[10,2370,2371,2372,873,2375,2377,2378,2380,2381,873,2383,2385,2386,2388],{},"On ordonne deux vecteurs ",[65,2373,2374],{},"composante par composante",[14,2376,876],{}," veut dire que chaque entrée de ",[14,2379,880],{}," est au plus égale à l'entrée correspondante de ",[14,2382,884],{},[14,2384,887],{}," veut dire ",[14,2387,876],{}," et qu'ils ne sont pas égaux.",[10,2390,2391,2392,1694,2394,2396,2397,2399],{},"C'est encore un ordre partiel, et c'est exactement pourquoi ça marche. Deux vecteurs peuvent être incomparables : ",[14,2393,897],{},[14,2395,900],{}," ont chacun une entrée plus grande que l'autre, donc aucun n'est ",[14,2398,904],{}," à l'autre. Le théorème :",[223,2401,2404],{"className":2402,"code":2403,"language":474},[472],"a → b    si et seulement si    V(a) \u003C V(b)\na ∥ b    si et seulement si    V(a) et V(b) sont incomparables\n",[14,2405,2403],{"__ignoreMap":228},[10,2407,2408,2409,2412,2413,2416,2417,2419,2420,2422,2423,2425,2426,2428,2429,2431,2432,2434,2435,2437,2438,2440,2441,2443,2444,2446,2447,489,2449,2451,2452,2454,2455,2457],{},"Notez le ",[35,2410,2411],{},"si et seulement si",". Contrairement à Lamport, les horloges vectorielles préservent l'ordre dans ",[65,2414,2415],{},"les deux"," sens. L'intuition : ",[14,2418,925],{}," compte les événements du processus ",[14,2421,929],{}," qui sont dans le passé causal de ",[14,2424,16],{},". Si ",[14,2427,16],{}," est dans le passé de ",[14,2430,20],{},", tout ce qui est dans le passé de ",[14,2433,16],{}," est aussi dans celui de ",[14,2436,20],{},", donc chaque entrée de ",[14,2439,948],{}," est au moins aussi grande, et ",[14,2442,20],{}," lui-même en ajoute une. Si ",[14,2445,16],{}," n'est pas dans le passé de ",[14,2448,20],{},[14,2450,20],{}," n'a pas vu au moins un événement que ",[14,2453,16],{}," a vu, et une entrée de ",[14,2456,948],{}," est plus petite. La comparaison tient alors en quelques lignes :",[223,2459,2460],{"className":225,"code":970,"language":227,"meta":228,"style":228},[14,2461,2462,2484,2502,2520,2538,2546,2550,2554,2572,2590,2598,2602,2606,2612,2624,2630,2638,2644,2652,2658,2662,2668],{"__ignoreMap":228},[232,2463,2464,2466,2468,2470,2472,2474,2476,2478,2480,2482],{"class":234,"line":235},[232,2465,277],{"class":238},[232,2467,979],{"class":296},[232,2469,363],{"class":238},[232,2471,16],{"class":283},[232,2473,160],{"class":238},[232,2475,988],{"class":283},[232,2477,685],{"class":242},[232,2479,293],{"class":238},[232,2481,995],{"class":242},[232,2483,375],{"class":238},[232,2485,2486,2488,2490,2492,2494,2496,2498,2500],{"class":234,"line":260},[232,2487,1002],{"class":249},[232,2489,160],{"class":238},[232,2491,1007],{"class":249},[232,2493,793],{"class":238},[232,2495,1013],{"class":1012},[232,2497,160],{"class":238},[232,2499,1013],{"class":1012},[232,2501,1020],{"class":270},[232,2503,2504,2506,2508,2510,2512,2514,2516,2518],{"class":234,"line":267},[232,2505,782],{"class":320},[232,2507,785],{"class":249},[232,2509,160],{"class":238},[232,2511,790],{"class":249},[232,2513,793],{"class":238},[232,2515,796],{"class":320},[232,2517,1037],{"class":249},[232,2519,396],{"class":238},[232,2521,2522,2524,2526,2528,2530,2532,2534,2536],{"class":234,"line":274},[232,2523,805],{"class":320},[232,2525,790],{"class":249},[232,2527,387],{"class":238},[232,2529,988],{"class":249},[232,2531,735],{"class":238},[232,2533,816],{"class":249},[232,2535,694],{"class":238},[232,2537,375],{"class":238},[232,2539,2540,2542,2544],{"class":234,"line":334},[232,2541,1062],{"class":249},[232,2543,409],{"class":238},[232,2545,1067],{"class":1012},[232,2547,2548],{"class":234,"line":339},[232,2549,842],{"class":238},[232,2551,2552],{"class":234,"line":345},[232,2553,418],{"class":238},[232,2555,2556,2558,2560,2562,2564,2566,2568,2570],{"class":234,"line":378},[232,2557,782],{"class":320},[232,2559,785],{"class":249},[232,2561,160],{"class":238},[232,2563,790],{"class":249},[232,2565,793],{"class":238},[232,2567,796],{"class":320},[232,2569,1092],{"class":249},[232,2571,396],{"class":238},[232,2573,2574,2576,2578,2580,2582,2584,2586,2588],{"class":234,"line":399},[232,2575,805],{"class":320},[232,2577,790],{"class":249},[232,2579,387],{"class":238},[232,2581,1105],{"class":249},[232,2583,735],{"class":238},[232,2585,816],{"class":249},[232,2587,694],{"class":238},[232,2589,375],{"class":238},[232,2591,2592,2594,2596],{"class":234,"line":415},[232,2593,1118],{"class":249},[232,2595,409],{"class":238},[232,2597,1067],{"class":1012},[232,2599,2600],{"class":234,"line":421},[232,2601,842],{"class":238},[232,2603,2604],{"class":234,"line":434},[232,2605,418],{"class":238},[232,2607,2608,2610],{"class":234,"line":447},[232,2609,1135],{"class":320},[232,2611,375],{"class":238},[232,2613,2614,2616,2618,2620,2622],{"class":234,"line":1140},[232,2615,1143],{"class":320},[232,2617,1146],{"class":249},[232,2619,1149],{"class":238},[232,2621,1152],{"class":249},[232,2623,1155],{"class":238},[232,2625,2626,2628],{"class":234,"line":1158},[232,2627,1161],{"class":320},[232,2629,1164],{"class":249},[232,2631,2632,2634,2636],{"class":234,"line":1167},[232,2633,1143],{"class":320},[232,2635,1172],{"class":249},[232,2637,1155],{"class":238},[232,2639,2640,2642],{"class":234,"line":1177},[232,2641,1161],{"class":320},[232,2643,1182],{"class":249},[232,2645,2646,2648,2650],{"class":234,"line":1185},[232,2647,1143],{"class":320},[232,2649,1152],{"class":249},[232,2651,1155],{"class":238},[232,2653,2654,2656],{"class":234,"line":1194},[232,2655,1161],{"class":320},[232,2657,1199],{"class":249},[232,2659,2660],{"class":234,"line":1202},[232,2661,418],{"class":238},[232,2663,2664,2666],{"class":234,"line":1207},[232,2665,437],{"class":320},[232,2667,1212],{"class":249},[232,2669,2670],{"class":234,"line":1215},[232,2671,331],{"class":238},[10,2673,2674],{},"Sur la même exécution que ci-dessus, les deux méthodes diffèrent exactement sur la paire qui compte :",[512,2676,2677,2688],{},[515,2678,2679],{},[518,2680,2681,2683,2685],{},[521,2682,2071],{},[521,2684,290],{},[521,2686,2687],{},"Horloge vectorielle",[534,2689,2690,2701,2711],{},[518,2691,2692,2695,2697],{},[539,2693,2694],{},"e3 (A envoie m1)",[539,2696,1243],{},[539,2698,2699],{},[14,2700,1248],{},[518,2702,2703,2705,2707],{},[539,2704,1253],{},[539,2706,582],{},[539,2708,2709],{},[14,2710,1260],{},[518,2712,2713,2716,2718],{},[539,2714,2715],{},"e7 (C reçoit m2)",[539,2717,593],{},[539,2719,2720],{},[14,2721,1272],{},[10,2723,2724,2725,2727,2728,2730,2731,2733],{},"Comparer e3 et e6 donne ",[65,2726,184],{}," (aucun vecteur n'est ",[14,2729,904],{}," à l'autre), alors que Lamport disait que e6 était venu avant. Comparer e3 et e7 donne ",[65,2732,1768],{},", ce qui est correct : e3 → e4 → e5 → e7 par les messages.",[451,2735,2737],{"id":2736},"vérifier-laffirmation","Vérifier l'affirmation",[10,2739,2740,2741,2744],{},"Je ne voulais pas me fier à la seule preuve. J'ai généré 10 000 exécutions aléatoires (4 processus, 40 pas, envois et réceptions aléatoires), calculé la vraie relation happened-before par fermeture transitive, et comparé. Résultat sur les 6,7 millions de paires : le verdict de l'horloge vectorielle a coïncidé avec la vraie relation ",[65,2742,2743],{},"à chaque fois",", paires concurrentes comprises.",[45,2746,2748],{"id":2747},"lusage-détecter-un-conflit-décriture","L'usage : détecter un conflit d'écriture",[10,2750,2751],{},"C'est le cas d'usage qui a rendu les horloges vectorielles célèbres : Dynamo d'Amazon, et les bases de sa lignée comme Riak. Deux réplicas acceptent des écritures sur le même panier à partir de la même version :",[223,2753,2755],{"className":225,"code":2754,"language":227,"meta":228,"style":228},"base := VClock{\"A\": 1}\nwa := base.Copy(); wa.Tick(\"A\")   \u002F\u002F le réplica A écrit : [A:2]\nwb := base.Copy(); wb.Tick(\"B\")   \u002F\u002F le réplica B écrit : [A:1 B:1]\nCompare(wa, wb)                   \u002F\u002F Concurrent\n",[14,2756,2757,2779,2812,2845],{"__ignoreMap":228},[232,2758,2759,2761,2763,2765,2767,2769,2771,2773,2775,2777],{"class":234,"line":235},[232,2760,1312],{"class":249},[232,2762,793],{"class":238},[232,2764,685],{"class":242},[232,2766,1319],{"class":238},[232,2768,1322],{"class":238},[232,2770,1326],{"class":1325},[232,2772,1322],{"class":238},[232,2774,1331],{"class":238},[232,2776,1335],{"class":1334},[232,2778,331],{"class":238},[232,2780,2781,2783,2785,2787,2789,2791,2793,2795,2797,2799,2801,2803,2805,2807,2809],{"class":234,"line":260},[232,2782,1342],{"class":249},[232,2784,793],{"class":238},[232,2786,1347],{"class":249},[232,2788,43],{"class":238},[232,2790,1352],{"class":296},[232,2792,1355],{"class":238},[232,2794,1358],{"class":249},[232,2796,43],{"class":238},[232,2798,1363],{"class":296},[232,2800,363],{"class":238},[232,2802,1322],{"class":238},[232,2804,1326],{"class":1325},[232,2806,1322],{"class":238},[232,2808,293],{"class":238},[232,2810,2811],{"class":270},"   \u002F\u002F le réplica A écrit : [A:2]\n",[232,2813,2814,2816,2818,2820,2822,2824,2826,2828,2830,2832,2834,2836,2838,2840,2842],{"class":234,"line":267},[232,2815,1381],{"class":249},[232,2817,793],{"class":238},[232,2819,1347],{"class":249},[232,2821,43],{"class":238},[232,2823,1352],{"class":296},[232,2825,1355],{"class":238},[232,2827,1394],{"class":249},[232,2829,43],{"class":238},[232,2831,1363],{"class":296},[232,2833,363],{"class":238},[232,2835,1322],{"class":238},[232,2837,1405],{"class":1325},[232,2839,1322],{"class":238},[232,2841,293],{"class":238},[232,2843,2844],{"class":270},"   \u002F\u002F le réplica B écrit : [A:1 B:1]\n",[232,2846,2847,2849,2851,2853,2855,2857,2859],{"class":234,"line":274},[232,2848,1417],{"class":296},[232,2850,363],{"class":238},[232,2852,1422],{"class":249},[232,2854,160],{"class":238},[232,2856,1394],{"class":249},[232,2858,293],{"class":238},[232,2860,1431],{"class":270},[10,2862,2863,2864,2866,2867,2870],{},"Aucune version n'est ",[14,2865,904],{}," à l'autre, donc le système sait qu'il détient ",[65,2868,2869],{},"deux siblings",", pas une version récente et une ancienne. Une règle « le plus récent gagne » sur l'horloge murale en aurait supprimé une sans prévenir personne. Ici la base peut remettre les deux à l'application pour fusion (par exemple l'union des articles du panier). Le client écrit ensuite la fusion avec une horloge qui domine les deux :",[223,2872,2873],{"className":225,"code":1444,"language":227,"meta":228,"style":228},[14,2874,2875,2911,2927],{"__ignoreMap":228},[232,2876,2877,2879,2881,2883,2885,2887,2889,2891,2893,2895,2897,2899,2901,2903,2905,2907,2909],{"class":234,"line":235},[232,2878,1451],{"class":249},[232,2880,793],{"class":238},[232,2882,1358],{"class":249},[232,2884,43],{"class":238},[232,2886,1352],{"class":296},[232,2888,1355],{"class":238},[232,2890,1464],{"class":249},[232,2892,43],{"class":238},[232,2894,1469],{"class":296},[232,2896,363],{"class":238},[232,2898,1322],{"class":238},[232,2900,1326],{"class":1325},[232,2902,1322],{"class":238},[232,2904,160],{"class":238},[232,2906,1394],{"class":249},[232,2908,293],{"class":238},[232,2910,1486],{"class":270},[232,2912,2913,2915,2917,2919,2921,2923,2925],{"class":234,"line":260},[232,2914,1417],{"class":296},[232,2916,363],{"class":238},[232,2918,1422],{"class":249},[232,2920,160],{"class":238},[232,2922,1464],{"class":249},[232,2924,293],{"class":238},[232,2926,1503],{"class":270},[232,2928,2929,2931,2933,2935,2937,2939,2941],{"class":234,"line":267},[232,2930,1417],{"class":296},[232,2932,363],{"class":238},[232,2934,1512],{"class":249},[232,2936,160],{"class":238},[232,2938,1464],{"class":249},[232,2940,293],{"class":238},[232,2942,1503],{"class":270},[10,2944,2945,2946,2949],{},"La version fusionnée est strictement supérieure aux deux siblings, donc le conflit est résolu et les deux anciennes versions peuvent être écartées. Gardez cette image en tête. L'opération « prendre le maximum composante par composante » est une ",[65,2947,2948],{},"jointure"," (join), le plus petit vecteur au-dessus des deux entrées. Elle est associative, commutative et idempotente, et le prochain article de la série, sur les CRDT, repose exactement sur cette propriété.",[45,2951,2953],{"id":2952},"coûts-et-alternatives","Coûts et alternatives",[139,2955,2956,2965,2971,2977],{},[77,2957,2958,2961,2962,2964],{},[65,2959,2960],{},"La taille croît avec le nombre de processus."," Un vecteur a une entrée par écrivain, donc chaque message et chaque valeur stockée porte ",[14,2963,1542],{}," de données en plus. Les systèmes avec beaucoup de clients éphémères ont besoin d'élagage ou de variantes plus fines comme les dotted version vectors.",[77,2966,2967,2970],{},[65,2968,2969],{},"Les écritures concurrentes demandent toujours une décision."," Détecter un conflit n'est pas le résoudre. Les systèmes à la Dynamo remettent les siblings à l'application, d'autres choisissent une règle comme last-writer-wins et acceptent de perdre des données.",[77,2972,2973,2976],{},[65,2974,2975],{},"Les horloges logiques hybrides (HLC)"," combinent un horodatage physique et un compteur à la Lamport. Elles gardent une taille constante et restent proches du temps réel, ce qui explique leur usage dans des systèmes comme CockroachDB. Comme les horloges de Lamport, elles préservent la causalité mais ne détectent pas la concurrence.",[77,2978,2979,2982],{},[65,2980,2981],{},"Les horloges physiques peuvent suffire si on y met le prix."," Spanner de Google borne l'incertitude d'horloge avec du matériel dédié et attend qu'elle passe. C'est une réponse d'ingénierie là où cet article donnait une réponse mathématique.",[45,2984,2986],{"id":2985},"à-retenir","À retenir",[139,2988,2989,2995,3002,3015,3030],{},[77,2990,2991,2992,2994],{},"Les horloges murales ne peuvent pas ordonner des événements entre machines. Ce qu'il faut, c'est la relation ",[65,2993,67],{},", un ordre partiel construit à partir de la séquence locale et des messages.",[77,2996,2997,2998,3001],{},"Dans un ordre partiel, certaines paires sont ",[65,2999,3000],{},"incomparables",". Ce sont les événements concurrents, et c'est là que vivent les conflits.",[77,3003,3004,3005,3008,3009,3011,3012,3014],{},"Les ",[65,3006,3007],{},"horloges de Lamport"," gardent un compteur : ",[14,3010,71],{}," implique ",[14,3013,502],{},", mais pas l'inverse. Dans mon test, elles ont fait paraître ordonnées 90 % des paires concurrentes. Utilisez-les quand n'importe quel ordre cohérent suffit.",[77,3016,3004,3017,3020,3021,3023,3024,3026,3027,3029],{},[65,3018,3019],{},"horloges vectorielles"," gardent un compteur par processus et se comparent composante par composante : ",[14,3022,71],{}," si et seulement si ",[14,3025,1604],{},". Elles détectent exactement la concurrence, pour un coût ",[14,3028,1542],{}," par message.",[77,3031,3032],{},"Le maximum composante par composante utilisé pour fusionner les horloges est une jointure, la même opération qui fait tourner les CRDT.",[10,3034,3035],{},"Prochain article de la série : les CRDT, et comment la jointure permet aux réplicas de fusionner sans conflit.",[1616,3037,1618],{},{"title":228,"searchDepth":260,"depth":260,"links":3039},[3040,3041,3046,3050,3051,3052],{"id":1687,"depth":260,"text":1688},{"id":1829,"depth":260,"text":1830,"children":3042},[3043,3044,3045],{"id":2011,"depth":267,"text":2012},{"id":2048,"depth":267,"text":2049},{"id":2148,"depth":267,"text":2149},{"id":2165,"depth":260,"text":2166,"children":3047},[3048,3049],{"id":2367,"depth":267,"text":2368},{"id":2736,"depth":267,"text":2737},{"id":2747,"depth":260,"text":2748},{"id":2952,"depth":260,"text":2953},{"id":2985,"depth":260,"text":2986},"Deux serveurs écrivent la même clé et vos logs disent que l'un est passé 3 ms avant l'autre. Peut-on s'y fier ? Pas sans horloge partagée. On construit les horloges de Lamport et vectorielles en Go, on explique le peu de maths derrière (ordres partiels, relation happened-before, comparaison composante par composante), et on teste les deux sur 6,7 millions de paires d'événements pour voir exactement ce que chacune peut et ne peut pas dire.",{},"\u002Fbackend\u002Fdistributed-systems\u002Flamport-vector-clocks.fr",{"title":1655,"description":3053},"horloges-lamport-vectorielles-go","2.backend\u002F4.distributed-systems\u002F1.lamport-vector-clocks.fr",[1645,1646,1647,1648],[1650,1651],"hlym-4odysW1Xy7NweIS87e7o0Oyu2xutdCTtVjWHvw",[3063,3066],{"title":3064,"path":3065},"Load Balancing Algorithms: Each One Fixes the Last One's Flaw","\u002Fbackend\u002Fload-balancing\u002Fload-balancing-algorithms",{"title":3067,"path":3068},"How a job interview led me to create Obscura - A password generator with real entropy","\u002Fsecurity\u002Fintroducing-obscura",[3070,3078,3087],{"path":3071,"title":3072,"description":3073,"date":3074,"tags":3075,"topics":3077},"\u002Fdsa\u002Fconsistent-hashing","Consistent Hashing in Go: Why hash(key) % N Breaks and How a Ring Fixes It","Add one server to a cache cluster that uses hash(key) % N and about 91% of your keys change owner overnight. Consistent hashing cuts that to roughly K\u002FN. We build the ring in Go, add virtual nodes, and measure real remapping and load balance, including the hash function mistake that quietly wrecks it.","2026-11-09",[3076,1646,1645,1648],"Consistent-Hashing",[1651,1650],{"path":3065,"title":3064,"description":3079,"date":3080,"tags":3081,"topics":3084},"Spreading requests across N servers sounds like a one-liner: pick a server, send the request. Then one backend is slower, or bigger, or holds a session, and the naive choice falls apart. This walks the classic algorithms as a chain where each one exists to fix the previous one's blind spot: round robin, weighted, least connections, power of two choices, and consistent hashing, in Go, ending with the failure every tutorial forgets.","2026-07-20",[3082,1646,1645,3083],"Load-Balancing","Resilience",[1650,3085,3086],"resilience","platform-engineering",{"path":3088,"title":3089,"description":3090,"date":3091,"tags":3092,"topics":3094},"\u002Fbackend\u002Fapi-design\u002Fretry-storms","Retry Storms: How Good Clients Take Down Healthy Servers","A retry looks harmless: the request failed, so try again. Multiply that by every client, add one slow dependency, and retries turn into a self-inflicted DDoS. This walks from the naive retry loop to exponential backoff, jitter, retry budgets and circuit breakers, the caller-side half of resilience that pairs with rate limiting on the server.","2026-07-11",[3083,1645,1646,3093],"Retries",[1650,3086,3085],1794697156230]