@mark1russell7/machines 0.3.30 → 0.3.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/build/index.d.ts +78 -0
- package/build/index.js +1200 -173
- package/build/index.js.map +1 -1
- package/package.json +4 -4
package/build/index.js
CHANGED
|
@@ -1,49 +1,177 @@
|
|
|
1
1
|
import { machineSchema } from "@mark1russell7/algebra/schema";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
//#region src/call.ts
|
|
4
|
+
/**
|
|
5
|
+
* This document is one call of a program on a socket of Linux TCP: a deadline for each try, and a
|
|
6
|
+
* retry after each deadline. It is a generic client that the program configures, as the
|
|
7
|
+
* `RetryAgent` of undici is configured with its options. No system sets its knobs, thus no knob has
|
|
8
|
+
* a default.
|
|
9
|
+
*
|
|
10
|
+
* - The deadline of a try is `timeout`. A timeout of 0 turns the deadline off, and the call waits
|
|
11
|
+
* for a layer below.
|
|
12
|
+
* - After a deadline, the program waits `backoff`, and the wait doubles at each retry up to
|
|
13
|
+
* `backoff_cap`. This backoff has no jitter.
|
|
14
|
+
* - The `retry` timer gives the time from the send of one try to the send of the next. Thus the sum of
|
|
15
|
+
* its first n waits is the start of try n + 1.
|
|
16
|
+
* - The count of retries is the count of firings of `retry`. The caller gives it.
|
|
17
|
+
*/
|
|
18
|
+
const DEADLINE_CALL = machineSchema.parse({
|
|
19
|
+
format: "mark1russell7.machine/1",
|
|
20
|
+
id: "deadline-call",
|
|
21
|
+
title: "One call of a program, with a deadline and retries after a capped exponential backoff",
|
|
22
|
+
layer: "self",
|
|
23
|
+
version: "1",
|
|
24
|
+
sources: [{
|
|
25
|
+
ref: "a deadline for each try, and retries after a backoff min(backoff × 2^n, cap), as a program configures them. The undici-retry-agent document of this package has the same backoff, min(minTimeout × timeoutFactor^n, maxTimeout)",
|
|
26
|
+
rung: "derived"
|
|
27
|
+
}],
|
|
28
|
+
states: {
|
|
29
|
+
POOLED: {
|
|
30
|
+
initial: true,
|
|
31
|
+
note: "the connection waits in the pool, and no call is open"
|
|
32
|
+
},
|
|
33
|
+
WAITING: { note: "the request is sent, and the call waits for the reply" },
|
|
34
|
+
DONE: { final: true },
|
|
35
|
+
FAILED: { final: true }
|
|
36
|
+
},
|
|
37
|
+
events: {
|
|
38
|
+
"app.send": { kind: "app" },
|
|
39
|
+
"rx.reply": { kind: "rx" },
|
|
40
|
+
"timer.deadline": { kind: "timer" },
|
|
41
|
+
"timer.retry": { kind: "timer" },
|
|
42
|
+
"socket.end": {
|
|
43
|
+
kind: "local",
|
|
44
|
+
note: "the socket below ends, with its errno"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
knobs: {
|
|
48
|
+
timeout: {
|
|
49
|
+
key: "the deadline of one try",
|
|
50
|
+
scope: "call",
|
|
51
|
+
unit: "ms",
|
|
52
|
+
default: null,
|
|
53
|
+
off: 0,
|
|
54
|
+
note: "the program sets it. 0 turns it off"
|
|
55
|
+
},
|
|
56
|
+
backoff: {
|
|
57
|
+
key: "the first wait between two tries",
|
|
58
|
+
scope: "call",
|
|
59
|
+
unit: "ms",
|
|
60
|
+
default: null,
|
|
61
|
+
note: "the program sets it"
|
|
62
|
+
},
|
|
63
|
+
backoff_cap: {
|
|
64
|
+
key: "the longest wait between two tries",
|
|
65
|
+
scope: "call",
|
|
66
|
+
unit: "ms",
|
|
67
|
+
default: null,
|
|
68
|
+
note: "the program sets it"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
timers: {
|
|
72
|
+
deadline: {
|
|
73
|
+
duration: {
|
|
74
|
+
knob: "timeout",
|
|
75
|
+
unit: "ms"
|
|
76
|
+
},
|
|
77
|
+
arms: ["enter:WAITING"],
|
|
78
|
+
cancels: ["rx.reply"],
|
|
79
|
+
fires: "timer.deadline"
|
|
80
|
+
},
|
|
81
|
+
retry: {
|
|
82
|
+
duration: {
|
|
83
|
+
expr: "timeout + min(backoff * pow(2, fired), backoff_cap)",
|
|
84
|
+
unit: "ms"
|
|
85
|
+
},
|
|
86
|
+
arms: ["timer.deadline"],
|
|
87
|
+
cancels: ["rx.reply"],
|
|
88
|
+
fires: "timer.retry",
|
|
89
|
+
backoff: { resets: ["enter:WAITING"] },
|
|
90
|
+
note: "the time from the send of one try to the send of the next: its deadline, then the wait"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
over: {
|
|
94
|
+
machine: "systems.machine.linux-tcp",
|
|
95
|
+
states: {
|
|
96
|
+
POOLED: "ESTABLISHED",
|
|
97
|
+
WAITING: "ESTABLISHED"
|
|
98
|
+
},
|
|
99
|
+
sets: {},
|
|
100
|
+
ends: "socket.end"
|
|
101
|
+
},
|
|
102
|
+
transitions: [
|
|
103
|
+
{
|
|
104
|
+
id: "dc.send",
|
|
105
|
+
from: "POOLED",
|
|
106
|
+
to: "WAITING",
|
|
107
|
+
on: "app.send"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: "dc.reply",
|
|
111
|
+
from: "WAITING",
|
|
112
|
+
to: "DONE",
|
|
113
|
+
on: "rx.reply"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "dc.deadline",
|
|
117
|
+
from: "WAITING",
|
|
118
|
+
to: "FAILED",
|
|
119
|
+
on: "timer.deadline",
|
|
120
|
+
errno: "ETIMEDOUT"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "dc.end",
|
|
124
|
+
from: ["POOLED", "WAITING"],
|
|
125
|
+
to: "FAILED",
|
|
126
|
+
on: "socket.end"
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
});
|
|
130
|
+
//#endregion
|
|
3
131
|
//#region src/grpc/grpc-go-server.ts
|
|
4
|
-
const AT$
|
|
132
|
+
const AT$18 = "grpc-go v1.84.0";
|
|
5
133
|
const GO = "go1.27.2 src/net";
|
|
6
134
|
const ETCD = "etcd v3.6.15";
|
|
7
135
|
/** These are the facts of the transport of grpc-go, for each server on it. */
|
|
8
136
|
const TRANSPORT = [
|
|
9
137
|
{
|
|
10
|
-
ref: `${AT$
|
|
138
|
+
ref: `${AT$18} internal/transport/defaults.go:31-40 the server keepalive: Time 2 h, Timeout 20 s. MinTime of the policy 5 min. MaxConnectionIdle, Age and AgeGrace infinite`,
|
|
11
139
|
rung: "implemented"
|
|
12
140
|
},
|
|
13
141
|
{
|
|
14
|
-
ref: `${AT$
|
|
142
|
+
ref: `${AT$18} internal/transport/http2_server.go:226-253 the defaults of the parameters, a jitter of 10 % on MaxConnectionAge, and TCP_USER_TIMEOUT = Timeout when Time is not infinite`,
|
|
15
143
|
rung: "implemented"
|
|
16
144
|
},
|
|
17
145
|
{
|
|
18
|
-
ref: `${AT$
|
|
146
|
+
ref: `${AT$18} internal/syscall/syscall_linux.go:71-90 SetTCPUserTimeout acts only on a *net.TCPConn. On any other net.Conn it does nothing`,
|
|
19
147
|
rung: "implemented"
|
|
20
148
|
},
|
|
21
149
|
{
|
|
22
|
-
ref: `${AT$
|
|
150
|
+
ref: `${AT$18} internal/transport/http2_server.go:1197-1286 keepalive(): a PING after Time with no read, and Close when no read comes for Timeout after it. MaxConnectionIdle and MaxConnectionAge drain with GOAWAY, and AgeGrace closes`,
|
|
23
151
|
rung: "implemented"
|
|
24
152
|
},
|
|
25
153
|
{
|
|
26
|
-
ref: `${AT$
|
|
154
|
+
ref: `${AT$18} internal/transport/http2_server.go:881-933 handlePing: a PING less than MinTime after the last one is a strike, and with no stream open and PermitWithoutStream false, less than 2 h. The third strike sends GOAWAY ENHANCE_YOUR_CALM too_many_pings and closes the connection`,
|
|
27
155
|
rung: "implemented"
|
|
28
156
|
},
|
|
29
157
|
{
|
|
30
|
-
ref: `${AT$
|
|
158
|
+
ref: `${AT$18} internal/transport/http2_server.go:279-281, :1064, :1132, :1176 each write of headers, a status or data by the server clears the strikes`,
|
|
31
159
|
rung: "implemented"
|
|
32
160
|
},
|
|
33
161
|
{
|
|
34
|
-
ref: `${AT$
|
|
162
|
+
ref: `${AT$18} internal/transport/http2_server.go:1291-1310 Close closes the net.Conn: a FIN, or a reset when bytes are unread`,
|
|
35
163
|
rung: "implemented"
|
|
36
164
|
},
|
|
37
165
|
{
|
|
38
|
-
ref: `${AT$
|
|
166
|
+
ref: `${AT$18} internal/transport/http2_server.go:536-537, :603-615 a call with the header grpc-timeout gets a timer of that timeout from the read of its headers. Its end closes the stream with RST_STREAM CANCEL, and the context of the handler ends`,
|
|
39
167
|
rung: "implemented"
|
|
40
168
|
},
|
|
41
169
|
{
|
|
42
|
-
ref: `${AT$
|
|
170
|
+
ref: `${AT$18} server.go:192, :1000-1009 a deadline of 120 s for the handshake of each new connection (ConnectionTimeout)`,
|
|
43
171
|
rung: "implemented"
|
|
44
172
|
},
|
|
45
173
|
{
|
|
46
|
-
ref: `${AT$
|
|
174
|
+
ref: `${AT$18} server.go:894-905 the comment of grpc-go: Go sets a TCP keepalive of 15 s on each accepted connection`,
|
|
47
175
|
rung: "documented"
|
|
48
176
|
},
|
|
49
177
|
{
|
|
@@ -702,7 +830,7 @@ const ETCD_SERVER = serverOf({
|
|
|
702
830
|
});
|
|
703
831
|
//#endregion
|
|
704
832
|
//#region src/grpc/grpc-js.ts
|
|
705
|
-
const AT$
|
|
833
|
+
const AT$17 = "@grpc/grpc-js 1.14.6 src";
|
|
706
834
|
/**
|
|
707
835
|
* This document is grpc-js 1.14.6 on Node, for one channel to one server with no TLS, its transport
|
|
708
836
|
* (one HTTP/2 session on one TCP connection) and one unary call on it. It gives the states of the
|
|
@@ -732,83 +860,83 @@ const GRPC_JS = machineSchema.parse({
|
|
|
732
860
|
},
|
|
733
861
|
sources: [
|
|
734
862
|
{
|
|
735
|
-
ref: `${AT$
|
|
863
|
+
ref: `${AT$17}/transport.ts:70 KEEPALIVE_TIMEOUT_MS is 20000`,
|
|
736
864
|
rung: "implemented"
|
|
737
865
|
},
|
|
738
866
|
{
|
|
739
|
-
ref: `${AT$
|
|
867
|
+
ref: `${AT$17}/transport.ts:183-198 grpc.keepalive_time_ms is -1 when the caller gives none, grpc.keepalive_timeout_ms is 20000, and grpc.keepalive_permit_without_calls is false`,
|
|
740
868
|
rung: "implemented"
|
|
741
869
|
},
|
|
742
870
|
{
|
|
743
|
-
ref: `${AT$
|
|
871
|
+
ref: `${AT$17}/transport.ts:447-453 a PING goes out only with a keepalive time above 0, and with a call in flight or permit_without_calls`,
|
|
744
872
|
rung: "implemented"
|
|
745
873
|
},
|
|
746
874
|
{
|
|
747
|
-
ref: `${AT$
|
|
875
|
+
ref: `${AT$17}/transport.ts:455-507 each PING starts a timer of keepalive_timeout_ms, and its end with no ACK ends the transport (handleDisconnect). A failed send of the PING ends it too`,
|
|
748
876
|
rung: "implemented"
|
|
749
877
|
},
|
|
750
878
|
{
|
|
751
|
-
ref: `${AT$
|
|
879
|
+
ref: `${AT$17}/transport.ts:515-534 and :553-561 the keepalive timer starts with the first call in flight, and after each ACK. It is not reset by data. When it ends with no call in flight, the PING goes out at the start of the next call`,
|
|
752
880
|
rung: "implemented"
|
|
753
881
|
},
|
|
754
882
|
{
|
|
755
|
-
ref: `${AT$
|
|
883
|
+
ref: `${AT$17}/transport.ts:200-242 a close or an error of the session, or a close of its socket, ends the transport. A GOAWAY makes it unusable (reportDisconnectToOwner)`,
|
|
756
884
|
rung: "implemented"
|
|
757
885
|
},
|
|
758
886
|
{
|
|
759
|
-
ref: `${AT$
|
|
887
|
+
ref: `${AT$17}/transport.ts:205-227 a GOAWAY ENHANCE_YOUR_CALM with the data too_many_pings tells the subchannel that the server refused the pings`,
|
|
760
888
|
rung: "implemented"
|
|
761
889
|
},
|
|
762
890
|
{
|
|
763
|
-
ref: `${AT$
|
|
891
|
+
ref: `${AT$17}/transport.ts:431-441 handleDisconnect: each call in flight gets onDisconnect, and the session is destroyed in the next turn of the event loop`,
|
|
764
892
|
rung: "implemented"
|
|
765
893
|
},
|
|
766
894
|
{
|
|
767
|
-
ref: `${AT$
|
|
895
|
+
ref: `${AT$17}/transport.ts:684-687 shutdown closes the session: a GOAWAY, then the end of the socket after the last stream`,
|
|
768
896
|
rung: "implemented"
|
|
769
897
|
},
|
|
770
898
|
{
|
|
771
|
-
ref: `${AT$
|
|
899
|
+
ref: `${AT$17}/transport.ts:792-810 the transport is ready at the first SETTINGS of the server (remoteSettings)`,
|
|
772
900
|
rung: "implemented"
|
|
773
901
|
},
|
|
774
902
|
{
|
|
775
|
-
ref: `${AT$
|
|
903
|
+
ref: `${AT$17}/transport.ts:818-840 the TCP connect is net.connect with no timeout`,
|
|
776
904
|
rung: "implemented"
|
|
777
905
|
},
|
|
778
906
|
{
|
|
779
|
-
ref: `${AT$
|
|
907
|
+
ref: `${AT$17}/transport.ts:862 setNoDelay on each connection: TCP_NODELAY, thus no Nagle. No setKeepAlive anywhere in the package`,
|
|
780
908
|
rung: "implemented"
|
|
781
909
|
},
|
|
782
910
|
{
|
|
783
|
-
ref: `${AT$
|
|
911
|
+
ref: `${AT$17}/subchannel-call.ts:360-371 onDisconnect ends the call with UNAVAILABLE 'Connection dropped' in the next turn of the event loop, unless the stream ends first`,
|
|
784
912
|
rung: "implemented"
|
|
785
913
|
},
|
|
786
914
|
{
|
|
787
|
-
ref: `${AT$
|
|
915
|
+
ref: `${AT$17}/subchannel-call.ts:230-329 the end of a stream with no status: REFUSED_STREAM is UNAVAILABLE, CANCEL after a dropped connection is UNAVAILABLE, and an internal error of ECONNRESET or ETIMEDOUT is UNAVAILABLE`,
|
|
788
916
|
rung: "implemented"
|
|
789
917
|
},
|
|
790
918
|
{
|
|
791
|
-
ref: `${AT$
|
|
919
|
+
ref: `${AT$17}/load-balancing-call.ts:257-261 a stream that the server refused has the progress REFUSED, and any other started stream PROCESSED`,
|
|
792
920
|
rung: "implemented"
|
|
793
921
|
},
|
|
794
922
|
{
|
|
795
|
-
ref: `${AT$
|
|
923
|
+
ref: `${AT$17}/retrying-call.ts:631-652 a transparent retry for NOT_STARTED, and one for REFUSED. A PROCESSED call is retried only by a retry policy of the service config, which the defaults do not have`,
|
|
796
924
|
rung: "implemented"
|
|
797
925
|
},
|
|
798
926
|
{
|
|
799
|
-
ref: `${AT$
|
|
927
|
+
ref: `${AT$17}/load-balancing-call.ts:338-345 a pick in TRANSIENT_FAILURE fails the call at once, unless it asks for waitForReady`,
|
|
800
928
|
rung: "implemented"
|
|
801
929
|
},
|
|
802
930
|
{
|
|
803
|
-
ref: `${AT$
|
|
931
|
+
ref: `${AT$17}/resolving-call.ts:118-180 the deadline of a call is a timer from the start of the call. Its end cancels the call with DEADLINE_EXCEEDED`,
|
|
804
932
|
rung: "implemented"
|
|
805
933
|
},
|
|
806
934
|
{
|
|
807
|
-
ref: `${AT$
|
|
935
|
+
ref: `${AT$17}/deadline.ts:39-52 the header grpc-timeout is the time left to the deadline when the call sends its headers, rounded up to whole milliseconds. A server can start a timer of it`,
|
|
808
936
|
rung: "implemented"
|
|
809
937
|
},
|
|
810
938
|
{
|
|
811
|
-
ref: `${AT$
|
|
939
|
+
ref: `${AT$17}/subchannel-call.ts:269-279 a RST_STREAM CANCEL of the server ends the call with CANCELLED 'Call cancelled'`,
|
|
812
940
|
rung: "implemented"
|
|
813
941
|
},
|
|
814
942
|
{
|
|
@@ -816,23 +944,23 @@ const GRPC_JS = machineSchema.parse({
|
|
|
816
944
|
rung: "observed"
|
|
817
945
|
},
|
|
818
946
|
{
|
|
819
|
-
ref: `${AT$
|
|
947
|
+
ref: `${AT$17}/subchannel.ts:275-291 the end of the transport takes the subchannel from READY to IDLE. After too_many_pings the keepalive time doubles`,
|
|
820
948
|
rung: "implemented"
|
|
821
949
|
},
|
|
822
950
|
{
|
|
823
|
-
ref: `${AT$
|
|
951
|
+
ref: `${AT$17}/subchannel.ts:298-304 a failed connect takes the subchannel to TRANSIENT_FAILURE`,
|
|
824
952
|
rung: "implemented"
|
|
825
953
|
},
|
|
826
954
|
{
|
|
827
|
-
ref: `${AT$
|
|
955
|
+
ref: `${AT$17}/backoff-timeout.ts:23-26 the reconnect waits 1000 ms first, each next wait 1.6 times longer, up to 120000 ms, with a jitter of 0.2`,
|
|
828
956
|
rung: "implemented"
|
|
829
957
|
},
|
|
830
958
|
{
|
|
831
|
-
ref: `${AT$
|
|
959
|
+
ref: `${AT$17}/backoff-timeout.ts:124, :169-181, :197-201 the first wait after a reset is the initial delay, with no jitter. Each next wait is the last wait, jitter included, times 1.6 and capped, then ± 20 %: the jitter compounds`,
|
|
832
960
|
rung: "implemented"
|
|
833
961
|
},
|
|
834
962
|
{
|
|
835
|
-
ref: `${AT$
|
|
963
|
+
ref: `${AT$17}/internal-channel.ts:74-77 and :324-325 a channel with no call for grpc.client_idle_timeout_ms goes IDLE: 30 min at the defaults, 1 s at least`,
|
|
836
964
|
rung: "implemented"
|
|
837
965
|
},
|
|
838
966
|
{
|
|
@@ -2586,7 +2714,7 @@ const UNDICI_CLIENT = machineSchema.parse({
|
|
|
2586
2714
|
});
|
|
2587
2715
|
//#endregion
|
|
2588
2716
|
//#region src/http/undici-retry.ts
|
|
2589
|
-
const AT$
|
|
2717
|
+
const AT$16 = "undici 8.12.2 lib/handler/retry-handler.js";
|
|
2590
2718
|
/**
|
|
2591
2719
|
* This document is one request through a `RetryAgent` of undici 8.12.2: the retries of a reply
|
|
2592
2720
|
* whose status is retryable, for example 429 or 503. Each fact comes from the published package at
|
|
@@ -2620,7 +2748,7 @@ const UNDICI_RETRY = machineSchema.parse({
|
|
|
2620
2748
|
},
|
|
2621
2749
|
sources: [
|
|
2622
2750
|
{
|
|
2623
|
-
ref: `${AT$
|
|
2751
|
+
ref: `${AT$16}:125-133 the defaults: retryAfter true, maxTimeout 30000, minTimeout 500, timeoutFactor 2, maxRetries 5, the methods GET HEAD OPTIONS PUT DELETE TRACE QUERY, the statusCodes 500 502 503 504 429`,
|
|
2624
2752
|
rung: "implemented"
|
|
2625
2753
|
},
|
|
2626
2754
|
{
|
|
@@ -2628,19 +2756,19 @@ const UNDICI_RETRY = machineSchema.parse({
|
|
|
2628
2756
|
rung: "implemented"
|
|
2629
2757
|
},
|
|
2630
2758
|
{
|
|
2631
|
-
ref: `${AT$
|
|
2759
|
+
ref: `${AT$16}:331-351 the policy: a method or a status out of its lists is not retried, and a counter above maxRetries gives up`,
|
|
2632
2760
|
rung: "implemented"
|
|
2633
2761
|
},
|
|
2634
2762
|
{
|
|
2635
|
-
ref: `${AT$
|
|
2763
|
+
ref: `${AT$16}:353-370 the delay: Retry-After in seconds times 1000, or an HTTP-date less the clock of the client (:13-16), capped by maxTimeout; 0 at once; else min(minTimeout * timeoutFactor^(counter - 1), maxTimeout); then setTimeout`,
|
|
2636
2764
|
rung: "implemented"
|
|
2637
2765
|
},
|
|
2638
2766
|
{
|
|
2639
|
-
ref: `${AT$
|
|
2767
|
+
ref: `${AT$16}:380 the counter: one more at each reply`,
|
|
2640
2768
|
rung: "implemented"
|
|
2641
2769
|
},
|
|
2642
2770
|
{
|
|
2643
|
-
ref: `${AT$
|
|
2771
|
+
ref: `${AT$16}:175-192 and :518-535 a retryable status with throwOnError drops the body of the reply, and the error goes to the policy`,
|
|
2644
2772
|
rung: "implemented"
|
|
2645
2773
|
},
|
|
2646
2774
|
{
|
|
@@ -2802,7 +2930,7 @@ const UNDICI_RETRY = machineSchema.parse({
|
|
|
2802
2930
|
});
|
|
2803
2931
|
//#endregion
|
|
2804
2932
|
//#region src/ioredis-sentinel.ts
|
|
2805
|
-
const AT$
|
|
2933
|
+
const AT$15 = "ioredis 6.0.0 built";
|
|
2806
2934
|
/**
|
|
2807
2935
|
* This document is the Sentinel path of ioredis 6.0.0. It tells how ioredis finds the primary of a
|
|
2808
2936
|
* group through the Sentinels, loses it, and connects to the new one. The standalone states of
|
|
@@ -2823,43 +2951,43 @@ const IOREDIS_SENTINEL = machineSchema.parse({
|
|
|
2823
2951
|
},
|
|
2824
2952
|
sources: [
|
|
2825
2953
|
{
|
|
2826
|
-
ref: `${AT$
|
|
2954
|
+
ref: `${AT$15}/connectors/SentinelConnector/index.js:46-121 connect (each Sentinel in turn, then sentinelRetryStrategy)`,
|
|
2827
2955
|
rung: "implemented"
|
|
2828
2956
|
},
|
|
2829
2957
|
{
|
|
2830
|
-
ref: `${AT$
|
|
2958
|
+
ref: `${AT$15}/connectors/SentinelConnector/index.js:145-151 resolveMaster (get-master-addr-by-name, then SENTINEL sentinels)`,
|
|
2831
2959
|
rung: "implemented"
|
|
2832
2960
|
},
|
|
2833
2961
|
{
|
|
2834
|
-
ref: `${AT$
|
|
2962
|
+
ref: `${AT$15}/connectors/SentinelConnector/index.js:175-212 connectToSentinel and resolve (one connection for each question, no retry)`,
|
|
2835
2963
|
rung: "implemented"
|
|
2836
2964
|
},
|
|
2837
2965
|
{
|
|
2838
|
-
ref: `${AT$
|
|
2966
|
+
ref: `${AT$15}/connectors/SentinelConnector/index.js:27-39 check (the role of the ready check)`,
|
|
2839
2967
|
rung: "implemented"
|
|
2840
2968
|
},
|
|
2841
2969
|
{
|
|
2842
|
-
ref: `${AT$
|
|
2970
|
+
ref: `${AT$15}/connectors/SentinelConnector/FailoverDetector.js:6-43 (+switch-master, only with failoverDetector)`,
|
|
2843
2971
|
rung: "implemented"
|
|
2844
2972
|
},
|
|
2845
2973
|
{
|
|
2846
|
-
ref: `${AT$
|
|
2974
|
+
ref: `${AT$15}/redis/RedisOptions.js:9-57 (the defaults)`,
|
|
2847
2975
|
rung: "implemented"
|
|
2848
2976
|
},
|
|
2849
2977
|
{
|
|
2850
|
-
ref: `${AT$
|
|
2978
|
+
ref: `${AT$15}/redis/event_handler.js:294-355 closeHandler (retryStrategy, then maxRetriesPerRequest flushes the queue)`,
|
|
2851
2979
|
rung: "implemented"
|
|
2852
2980
|
},
|
|
2853
2981
|
{
|
|
2854
|
-
ref: `${AT$
|
|
2982
|
+
ref: `${AT$15}/redis/event_handler.js:364-449 readyHandler (retryAttempts back to 0, the resend, the offline queue)`,
|
|
2855
2983
|
rung: "implemented"
|
|
2856
2984
|
},
|
|
2857
2985
|
{
|
|
2858
|
-
ref: `${AT$
|
|
2986
|
+
ref: `${AT$15}/Redis.js:123-240 _connect (connectTimeout, keepAlive) and :379-405 sendCommand (the offline queue)`,
|
|
2859
2987
|
rung: "implemented"
|
|
2860
2988
|
},
|
|
2861
2989
|
{
|
|
2862
|
-
ref: `${AT$
|
|
2990
|
+
ref: `${AT$15}/Redis.js:599-605 handleReconnection (reconnectOnError: null, thus a READONLY error goes to the caller)`,
|
|
2863
2991
|
rung: "implemented"
|
|
2864
2992
|
}
|
|
2865
2993
|
],
|
|
@@ -3664,10 +3792,19 @@ var linux_tcp_default = {
|
|
|
3664
3792
|
],
|
|
3665
3793
|
"unit": "s"
|
|
3666
3794
|
},
|
|
3795
|
+
"early": {
|
|
3796
|
+
"model": "linux.syn-giveup-early",
|
|
3797
|
+
"knobs": [
|
|
3798
|
+
"tcp_syn_retries",
|
|
3799
|
+
"tcp_syn_linear_timeouts",
|
|
3800
|
+
"hz"
|
|
3801
|
+
],
|
|
3802
|
+
"unit": "s"
|
|
3803
|
+
},
|
|
3667
3804
|
"arms": ["enter:SYN_SENT"],
|
|
3668
3805
|
"cancels": ["rx.synack", "rx.rst"],
|
|
3669
3806
|
"fires": "timer.syn.exhausted",
|
|
3670
|
-
"note": "6.6: linear 1 s waits for the first tcp_syn_linear_timeouts retransmissions, then doubling; 131 s at the defaults. Each wait is a timer of the wheel, rounded up at its level: up to 6.08 s late in all at HZ 1000, because the wait of 64 s can be 4.096 s late"
|
|
3807
|
+
"note": "6.6: linear 1 s waits for the first tcp_syn_linear_timeouts retransmissions, then doubling; 131 s at the defaults. A firing gives up by count, after tcp_syn_retries + tcp_syn_linear_timeouts retransmissions, or by time, after the budget of tcp_model_timeout for tcp_syn_retries alone: 7 s at tcp_syn_retries 2, before the count of 11 s. Each wait is a timer of the wheel, rounded up at its level: up to 6.08 s late in all at HZ 1000, because the wait of 64 s can be 4.096 s late"
|
|
3671
3808
|
},
|
|
3672
3809
|
"syn_retransmit": {
|
|
3673
3810
|
"duration": {
|
|
@@ -5052,226 +5189,1093 @@ const IOREDIS_STATUS = machineSchema.parse(ioredis_status_default);
|
|
|
5052
5189
|
*/
|
|
5053
5190
|
const MANIFESTATIONS = z.record(z.string(), z.array(z.string())).parse(manifestations_default);
|
|
5054
5191
|
//#endregion
|
|
5055
|
-
//#region src/
|
|
5056
|
-
const AT$
|
|
5192
|
+
//#region src/k8s/kubelet-readiness.ts
|
|
5193
|
+
const AT$14 = "kubernetes v1.37.0";
|
|
5194
|
+
const WORKER = `${AT$14} pkg/kubelet/prober/worker.go`;
|
|
5057
5195
|
/**
|
|
5058
|
-
* This document is the
|
|
5059
|
-
*
|
|
5060
|
-
*
|
|
5061
|
-
* catch-up of a new primary and a step-down.
|
|
5196
|
+
* This document is the readiness probe of the kubelet of Kubernetes 1.37.0 for one container. Each
|
|
5197
|
+
* fact comes from the source at the tag v1.37.0, with the file and the line. The defaults are a
|
|
5198
|
+
* period of 10 s, a timeout of 1 s and a failure threshold of 3.
|
|
5062
5199
|
*
|
|
5063
|
-
*
|
|
5064
|
-
*
|
|
5065
|
-
*
|
|
5066
|
-
*
|
|
5067
|
-
*
|
|
5200
|
+
* - The worker probes once at its start, then once at each tick of a Go ticker of one period. The
|
|
5201
|
+
* probe is synchronous. A Go ticker keeps one tick for a slow receiver, thus a probe that takes
|
|
5202
|
+
* longer than a period starts the next probe at once. The interval is max(period, timeout).
|
|
5203
|
+
* - A probe of a pod that stops to answer fails at its timeout. An HTTP probe that times out is a
|
|
5204
|
+
* failure without an error, thus the kubelet does not try it again.
|
|
5205
|
+
* - The result changes after `threshold` failures in a row. Thus the pod becomes unready at the
|
|
5206
|
+
* timeout of probe `threshold`, counted from the first probe after the fault.
|
|
5207
|
+
* - The first probe after the fault comes within one period. Thus the band is one period wide.
|
|
5208
|
+
* - A tcpSocket probe passes on a stalled process, because its kernel accepts the connection.
|
|
5068
5209
|
*
|
|
5069
|
-
*
|
|
5070
|
-
*
|
|
5071
|
-
*
|
|
5072
|
-
* position of that secondary only from the response to each heartbeat.
|
|
5210
|
+
* This document does not hold two delays of the start of a container. The probe waits for
|
|
5211
|
+
* `initialDelaySeconds` after the start. A kubelet that started less than one period ago sleeps a
|
|
5212
|
+
* random part of a period before its first probe.
|
|
5073
5213
|
*/
|
|
5074
|
-
const
|
|
5214
|
+
const KUBELET_READINESS = machineSchema.parse({
|
|
5075
5215
|
format: "mark1russell7.machine/1",
|
|
5076
|
-
id: "
|
|
5077
|
-
title: "
|
|
5078
|
-
layer: "
|
|
5079
|
-
version: "
|
|
5216
|
+
id: "kubelet-readiness",
|
|
5217
|
+
title: "The readiness probe of the kubelet of Kubernetes 1.37.0, for one container",
|
|
5218
|
+
layer: "canary",
|
|
5219
|
+
version: "1.37.0",
|
|
5080
5220
|
conditions: {
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
members: "three members, each with one vote and priority 1"
|
|
5221
|
+
kubernetes: "1.37.0",
|
|
5222
|
+
go: "1.26.6"
|
|
5084
5223
|
},
|
|
5085
5224
|
sources: [
|
|
5086
5225
|
{
|
|
5087
|
-
ref: `${AT$
|
|
5088
|
-
rung: "implemented"
|
|
5089
|
-
},
|
|
5090
|
-
{
|
|
5091
|
-
ref: `${AT$13}/repl_server_parameters.idl:320-328 replElectionTimeoutOffsetLimitFraction 0.15`,
|
|
5226
|
+
ref: `${AT$14} pkg/apis/core/v1/defaults.go:275-288 SetDefaults_Probe: TimeoutSeconds 1, PeriodSeconds 10, SuccessThreshold 1, FailureThreshold 3. InitialDelaySeconds has no default, thus 0`,
|
|
5092
5227
|
rung: "implemented"
|
|
5093
5228
|
},
|
|
5094
5229
|
{
|
|
5095
|
-
ref: `${AT$
|
|
5096
|
-
rung: "
|
|
5230
|
+
ref: `${AT$14} staging/src/k8s.io/api/core/v1/types.go:2936-2972 type Probe: initialDelaySeconds (:2942), timeoutSeconds with a minimum of 1 (:2947), periodSeconds with a minimum of 1 (:2951), successThreshold (:2955), failureThreshold with a minimum of 1 (:2959)`,
|
|
5231
|
+
rung: "documented"
|
|
5097
5232
|
},
|
|
5098
5233
|
{
|
|
5099
|
-
ref: `${
|
|
5234
|
+
ref: `${WORKER}:113-116 a readiness probe starts with the result Failure`,
|
|
5100
5235
|
rung: "implemented"
|
|
5101
5236
|
},
|
|
5102
5237
|
{
|
|
5103
|
-
ref: `${
|
|
5238
|
+
ref: `${WORKER}:157-202 run: a sleep of rand.Float64() × period if the kubelet started less than one period ago (:162-167), a ticker of periodSeconds (:160, :169), and one probe at once, then one probe at each tick (:187-200)`,
|
|
5104
5239
|
rung: "implemented"
|
|
5105
5240
|
},
|
|
5106
5241
|
{
|
|
5107
|
-
ref: `${
|
|
5242
|
+
ref: `${WORKER}:330-333 the probe is off for InitialDelaySeconds after the start of the container`,
|
|
5108
5243
|
rung: "implemented"
|
|
5109
5244
|
},
|
|
5110
5245
|
{
|
|
5111
|
-
ref: `${
|
|
5246
|
+
ref: `${WORKER}:366-379 resultRun counts the equal results in a row. The result changes only at failureThreshold failures or successThreshold successes`,
|
|
5112
5247
|
rung: "implemented"
|
|
5113
5248
|
},
|
|
5114
5249
|
{
|
|
5115
|
-
ref: `${AT$
|
|
5250
|
+
ref: `${AT$14} pkg/kubelet/prober/prober.go:42, :139-150, :154 a probe runs again up to 3 times only on an error, and its timeout is TimeoutSeconds`,
|
|
5116
5251
|
rung: "implemented"
|
|
5117
5252
|
},
|
|
5118
5253
|
{
|
|
5119
|
-
ref: `${AT$
|
|
5254
|
+
ref: `${AT$14} pkg/probe/http/http.go:91-104, :119-125 an http.Client with the timeout, and an error of client.Do is a Failure without an error`,
|
|
5120
5255
|
rung: "implemented"
|
|
5121
5256
|
},
|
|
5122
5257
|
{
|
|
5123
|
-
ref: `${AT$
|
|
5258
|
+
ref: `${AT$14} pkg/probe/tcp/tcp.go:50-62 DoTCPProbe dials with the timeout, and a connection is a Success`,
|
|
5124
5259
|
rung: "implemented"
|
|
5125
5260
|
},
|
|
5126
5261
|
{
|
|
5127
|
-
ref:
|
|
5262
|
+
ref: "go1.26.6 src/time/tick.go:21-24, :40-41 NewTicker: a channel of one tick, and the ticker drops ticks for a slow receiver",
|
|
5128
5263
|
rung: "implemented"
|
|
5264
|
+
}
|
|
5265
|
+
],
|
|
5266
|
+
states: {
|
|
5267
|
+
READY: {
|
|
5268
|
+
initial: true,
|
|
5269
|
+
note: "the probes pass"
|
|
5129
5270
|
},
|
|
5130
|
-
{
|
|
5131
|
-
|
|
5132
|
-
|
|
5271
|
+
UNREADY: { note: "the kubelet sets the result Failure, and the endpoints of the Service drop the pod" }
|
|
5272
|
+
},
|
|
5273
|
+
events: {
|
|
5274
|
+
"probe.fail": {
|
|
5275
|
+
kind: "local",
|
|
5276
|
+
note: "a probe fails at its timeout, or with a failure status"
|
|
5133
5277
|
},
|
|
5134
|
-
{
|
|
5135
|
-
|
|
5136
|
-
|
|
5278
|
+
"probe.pass": { kind: "local" },
|
|
5279
|
+
"timer.probe": { kind: "timer" },
|
|
5280
|
+
"timer.unready": { kind: "timer" }
|
|
5281
|
+
},
|
|
5282
|
+
knobs: {
|
|
5283
|
+
period: {
|
|
5284
|
+
key: "readinessProbe.periodSeconds",
|
|
5285
|
+
scope: "pod",
|
|
5286
|
+
unit: "s",
|
|
5287
|
+
default: 10
|
|
5137
5288
|
},
|
|
5138
|
-
{
|
|
5139
|
-
|
|
5140
|
-
|
|
5289
|
+
threshold: {
|
|
5290
|
+
key: "readinessProbe.failureThreshold",
|
|
5291
|
+
scope: "pod",
|
|
5292
|
+
unit: "count",
|
|
5293
|
+
default: 3
|
|
5141
5294
|
},
|
|
5142
|
-
{
|
|
5143
|
-
|
|
5144
|
-
|
|
5295
|
+
timeout: {
|
|
5296
|
+
key: "readinessProbe.timeoutSeconds",
|
|
5297
|
+
scope: "pod",
|
|
5298
|
+
unit: "s",
|
|
5299
|
+
default: 1
|
|
5300
|
+
}
|
|
5301
|
+
},
|
|
5302
|
+
timers: {
|
|
5303
|
+
probe: {
|
|
5304
|
+
duration: {
|
|
5305
|
+
expr: "max(period, timeout)",
|
|
5306
|
+
unit: "s"
|
|
5307
|
+
},
|
|
5308
|
+
arms: ["enter:READY"],
|
|
5309
|
+
fires: "timer.probe",
|
|
5310
|
+
backoff: { resets: ["enter:READY"] },
|
|
5311
|
+
note: "worker.go:187-200: the next probe, at the next tick. A probe longer than a period gets the kept tick at once, the same each time"
|
|
5145
5312
|
},
|
|
5313
|
+
unready: {
|
|
5314
|
+
duration: {
|
|
5315
|
+
expr: "period + (threshold - 1) * max(period, timeout) + timeout",
|
|
5316
|
+
unit: "s"
|
|
5317
|
+
},
|
|
5318
|
+
early: {
|
|
5319
|
+
knob: "period",
|
|
5320
|
+
unit: "s"
|
|
5321
|
+
},
|
|
5322
|
+
arms: ["probe.fail"],
|
|
5323
|
+
cancels: ["probe.pass"],
|
|
5324
|
+
fires: "timer.unready",
|
|
5325
|
+
note: "from the fault: the first probe within one period, threshold - 1 intervals, then the timeout of the last probe. A probe in flight at the fault gets its reply within milliseconds, thus the band does not hold it"
|
|
5326
|
+
}
|
|
5327
|
+
},
|
|
5328
|
+
transitions: [{
|
|
5329
|
+
id: "kr.probe",
|
|
5330
|
+
from: "READY",
|
|
5331
|
+
to: "READY",
|
|
5332
|
+
on: ["timer.probe", "probe.pass"],
|
|
5333
|
+
timer: "probe",
|
|
5334
|
+
source: "worker.go:187-200"
|
|
5335
|
+
}, {
|
|
5336
|
+
id: "kr.unready",
|
|
5337
|
+
from: "READY",
|
|
5338
|
+
to: "UNREADY",
|
|
5339
|
+
on: "timer.unready",
|
|
5340
|
+
timer: "unready",
|
|
5341
|
+
source: "worker.go:366-379"
|
|
5342
|
+
}]
|
|
5343
|
+
});
|
|
5344
|
+
//#endregion
|
|
5345
|
+
//#region src/kafka/group.ts
|
|
5346
|
+
const GROUP = "kafka 4.3.2 group-coordinator/src/main/java/org/apache/kafka/coordinator/group";
|
|
5347
|
+
const INTERNALS = "kafka 4.3.2 clients/src/main/java/org/apache/kafka/clients/consumer/internals";
|
|
5348
|
+
/**
|
|
5349
|
+
* This document is Apache Kafka 4.3.2, for one member of a consumer group that a process stop holds.
|
|
5350
|
+
* It gives when the coordinator removes the member, and when another member gets its partition. It
|
|
5351
|
+
* also gives the answer to a commit of the old offsets after the stop.
|
|
5352
|
+
*
|
|
5353
|
+
* In the consumer protocol the session is a setting of the broker. Each heartbeat starts the session
|
|
5354
|
+
* timer again. The last heartbeat came up to one interval before the stop, thus the fence comes one
|
|
5355
|
+
* session after the stop, less up to one interval. The other member takes the partition at one of its
|
|
5356
|
+
* own heartbeats after the fence.
|
|
5357
|
+
*
|
|
5358
|
+
* A commit of the member after the fence gets UNKNOWN_MEMBER_ID, and the client throws
|
|
5359
|
+
* CommitFailedException. The rejoin changes this. A member that polls after the fence joins again at
|
|
5360
|
+
* epoch 0 and gets a new epoch. The commit check at the same epoch asks no question about who owns the
|
|
5361
|
+
* partition. Thus the broker accepts a commit of the old offsets at the new epoch. That commit moves
|
|
5362
|
+
* the committed offset back while another member owns the partition.
|
|
5363
|
+
*/
|
|
5364
|
+
const KAFKA_CONSUMER_GROUP = machineSchema.parse({
|
|
5365
|
+
format: "mark1russell7.machine/1",
|
|
5366
|
+
id: "kafka-consumer-group",
|
|
5367
|
+
title: "Apache Kafka 4.3.2: one member of a consumer group under a process stop, its fence, the takeover of its partition and its commit after the stop",
|
|
5368
|
+
layer: "server",
|
|
5369
|
+
version: "4.3.2",
|
|
5370
|
+
conditions: {
|
|
5371
|
+
kafka: "4.3.2",
|
|
5372
|
+
mode: "KRaft, with one controller that is not a broker",
|
|
5373
|
+
group: "two members and two partitions, with one partition for each member before the stop",
|
|
5374
|
+
fault: "a stop of the process of one member, for a time, in the processing of one batch"
|
|
5375
|
+
},
|
|
5376
|
+
sources: [
|
|
5146
5377
|
{
|
|
5147
|
-
ref: `${
|
|
5378
|
+
ref: `${GROUP}/GroupCoordinatorConfig.java:184-204 the session of the consumer protocol is 45000, its minimum 45000, its maximum 60000, and the heartbeat interval is 5000`,
|
|
5148
5379
|
rung: "implemented"
|
|
5149
5380
|
},
|
|
5150
5381
|
{
|
|
5151
|
-
ref: `${
|
|
5382
|
+
ref: `${GROUP}/GroupCoordinatorConfig.java:592-598 the session is at or above its minimum and at or below its maximum, and the interval is below the session`,
|
|
5152
5383
|
rung: "implemented"
|
|
5153
5384
|
},
|
|
5154
5385
|
{
|
|
5155
|
-
ref: `${
|
|
5386
|
+
ref: `${GROUP}/GroupCoordinatorConfig.java:163-168 the classic session is between 6000 and 1800000, a value that the client sets`,
|
|
5156
5387
|
rung: "implemented"
|
|
5157
5388
|
},
|
|
5158
5389
|
{
|
|
5159
|
-
ref: `${
|
|
5390
|
+
ref: `${GROUP}/GroupMetadataManager.java:2475 each heartbeat schedules the session again, and :4775-4787 the timer fences the member`,
|
|
5160
5391
|
rung: "implemented"
|
|
5161
5392
|
},
|
|
5162
5393
|
{
|
|
5163
|
-
ref: `${
|
|
5394
|
+
ref: `${GROUP}/GroupMetadataManager.java:4687 the log line of a fence of the consumer protocol, and :7026 the line of a classic member`,
|
|
5164
5395
|
rung: "implemented"
|
|
5165
5396
|
},
|
|
5166
5397
|
{
|
|
5167
|
-
ref: `${
|
|
5398
|
+
ref: `${GROUP}/GroupMetadataManager.java:4358 the fence removes the member, raises the group epoch and cancels the timers of the member`,
|
|
5168
5399
|
rung: "implemented"
|
|
5169
5400
|
},
|
|
5170
5401
|
{
|
|
5171
|
-
ref: `${
|
|
5402
|
+
ref: `${GROUP}/GroupMetadataManager.java:3867 and :3900-3947 the next heartbeat of another member computes the target and reconciles`,
|
|
5172
5403
|
rung: "implemented"
|
|
5173
5404
|
},
|
|
5174
5405
|
{
|
|
5175
|
-
ref: `${
|
|
5406
|
+
ref: `${GROUP}/modern/consumer/ConsumerGroup.java:649-690 a commit of a member that is gone gets UNKNOWN_MEMBER_ID, and a commit at the epoch of the member passes with no check of who owns the partition`,
|
|
5176
5407
|
rung: "implemented"
|
|
5177
5408
|
},
|
|
5178
5409
|
{
|
|
5179
|
-
ref:
|
|
5410
|
+
ref: `${INTERNALS}/CommitRequestManager.java:826-832 a commit sends the member and the epoch, and :901-905 UNKNOWN_MEMBER_ID becomes CommitFailedException`,
|
|
5180
5411
|
rung: "implemented"
|
|
5181
5412
|
},
|
|
5182
5413
|
{
|
|
5183
|
-
ref: `${
|
|
5414
|
+
ref: `${INTERNALS}/AbstractHeartbeatRequestManager.java:427-442 a fenced epoch and an unknown member both fence the client`,
|
|
5184
5415
|
rung: "implemented"
|
|
5185
5416
|
},
|
|
5186
5417
|
{
|
|
5187
|
-
ref: `${
|
|
5418
|
+
ref: `${INTERNALS}/AbstractMembershipManager.java:388 a fenced client resets its epoch, signals the lost partitions, clears its assignment and joins again`,
|
|
5188
5419
|
rung: "implemented"
|
|
5189
5420
|
},
|
|
5190
5421
|
{
|
|
5191
|
-
ref: `${
|
|
5422
|
+
ref: `${INTERNALS}/ConsumerMembershipManager.java:367-374 with a listener, the lost callback waits for the poll of the application`,
|
|
5192
5423
|
rung: "implemented"
|
|
5193
5424
|
},
|
|
5194
5425
|
{
|
|
5195
|
-
ref: `${
|
|
5426
|
+
ref: `${INTERNALS}/AsyncKafkaConsumer.java:1006 a poll runs the callbacks, thus the rejoin follows the poll`,
|
|
5196
5427
|
rung: "implemented"
|
|
5197
5428
|
},
|
|
5198
5429
|
{
|
|
5199
|
-
ref:
|
|
5430
|
+
ref: `${INTERNALS}/ConsumerCoordinator.java:1312-1325 and :1433-1448 a classic commit with no generation, or with UNKNOWN_MEMBER_ID, gets CommitFailedException`,
|
|
5200
5431
|
rung: "implemented"
|
|
5201
5432
|
}
|
|
5202
5433
|
],
|
|
5203
5434
|
states: {
|
|
5204
|
-
|
|
5435
|
+
stable: {
|
|
5205
5436
|
initial: true,
|
|
5206
|
-
|
|
5437
|
+
note: "the coordinator holds the member, and the member holds its partition"
|
|
5207
5438
|
},
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
candidate: { note: "the real election: a new term, a durable vote for itself, and a vote request to each member" },
|
|
5211
|
-
catchup: { note: "PRIMARY in catch-up: it waits for a heartbeat answer from each other member, or a failure (log 21359)" },
|
|
5212
|
-
drain: { note: "PRIMARY in drain mode: the applier ends its buffer, and the hello says secondary (replication_coordinator_impl.cpp:2580-2584)" },
|
|
5213
|
-
primary: { note: "PRIMARY that accepts writes (log 21331)" },
|
|
5214
|
-
"stepping-down": { note: "replSetStepDown waits for a caught-up secondary, and the hello says not writable (step_up_step_down.cpp:46-52)" }
|
|
5439
|
+
fenced: { note: "the coordinator removed the member, and the stopped client does not know" },
|
|
5440
|
+
rejoined: { note: "the client joined again and has a new epoch, and another member can own its old partition" }
|
|
5215
5441
|
},
|
|
5216
5442
|
events: {
|
|
5217
|
-
"tx.heartbeat": {
|
|
5218
|
-
kind: "tx",
|
|
5219
|
-
note: "a heartbeat request, the first of a window or a retry in it"
|
|
5220
|
-
},
|
|
5221
5443
|
"rx.heartbeat": {
|
|
5222
5444
|
kind: "rx",
|
|
5223
|
-
note: "a heartbeat
|
|
5224
|
-
},
|
|
5225
|
-
"rx.primary-heartbeat": {
|
|
5226
|
-
kind: "rx",
|
|
5227
|
-
note: "a heartbeat response from the primary in the current term"
|
|
5445
|
+
note: "a heartbeat of this member reaches the coordinator"
|
|
5228
5446
|
},
|
|
5229
|
-
"rx.
|
|
5447
|
+
"rx.commit": {
|
|
5230
5448
|
kind: "rx",
|
|
5231
|
-
note: "
|
|
5449
|
+
note: "a commit of the old offsets, at the epoch of the member"
|
|
5232
5450
|
},
|
|
5233
|
-
"rx.
|
|
5451
|
+
"rx.commit-after-fence": {
|
|
5234
5452
|
kind: "rx",
|
|
5235
|
-
note: "
|
|
5453
|
+
note: "a commit of the old offsets from a member that the coordinator removed"
|
|
5236
5454
|
},
|
|
5237
|
-
"
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
note: "the window ends without a response: the target is DOWN (log 21216)"
|
|
5455
|
+
"local.poll": {
|
|
5456
|
+
kind: "local",
|
|
5457
|
+
note: "the application polls after the thaw"
|
|
5241
5458
|
},
|
|
5242
|
-
"timer.
|
|
5459
|
+
"timer.session": {
|
|
5243
5460
|
kind: "timer",
|
|
5244
|
-
note: "
|
|
5461
|
+
note: "the session of the member ends in the coordinator"
|
|
5245
5462
|
},
|
|
5246
|
-
"timer.
|
|
5463
|
+
"timer.takeover": {
|
|
5247
5464
|
kind: "timer",
|
|
5248
|
-
note: "
|
|
5465
|
+
note: "a heartbeat of the other member after the fence gives it the partition"
|
|
5466
|
+
}
|
|
5467
|
+
},
|
|
5468
|
+
knobs: {
|
|
5469
|
+
consumer_session_timeout: {
|
|
5470
|
+
key: "group.consumer.session.timeout.ms",
|
|
5471
|
+
scope: "broker",
|
|
5472
|
+
unit: "ms",
|
|
5473
|
+
default: 45e3,
|
|
5474
|
+
note: "GroupCoordinatorConfig.java:184"
|
|
5249
5475
|
},
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5476
|
+
consumer_heartbeat_interval: {
|
|
5477
|
+
key: "group.consumer.heartbeat.interval.ms",
|
|
5478
|
+
scope: "broker",
|
|
5479
|
+
unit: "ms",
|
|
5480
|
+
default: 5e3,
|
|
5481
|
+
note: "GroupCoordinatorConfig.java:196"
|
|
5253
5482
|
},
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5483
|
+
classic_session_timeout: {
|
|
5484
|
+
key: "session.timeout.ms",
|
|
5485
|
+
scope: "client",
|
|
5486
|
+
unit: "ms",
|
|
5487
|
+
default: 45e3,
|
|
5488
|
+
note: "a value of the client, between the limits at GroupCoordinatorConfig.java:163-168"
|
|
5257
5489
|
},
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5490
|
+
classic_heartbeat_interval: {
|
|
5491
|
+
key: "heartbeat.interval.ms",
|
|
5492
|
+
scope: "client",
|
|
5493
|
+
unit: "ms",
|
|
5494
|
+
default: 3e3,
|
|
5495
|
+
note: "a value of the client"
|
|
5261
5496
|
},
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5497
|
+
early_slack: {
|
|
5498
|
+
key: "the time from a heartbeat of the client to its arrival at the coordinator",
|
|
5499
|
+
scope: "broker",
|
|
5500
|
+
unit: "ms",
|
|
5501
|
+
default: 100,
|
|
5502
|
+
note: "an assumption of this document, not a value of Kafka"
|
|
5266
5503
|
},
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5504
|
+
late_slack: {
|
|
5505
|
+
key: "the time from the end of a session to the line of the fence, and to the takeover",
|
|
5506
|
+
scope: "broker",
|
|
5507
|
+
unit: "ms",
|
|
5508
|
+
default: 1e3,
|
|
5509
|
+
note: "an assumption of this document, not a value of Kafka: the timer wheel and the write of the coordinator"
|
|
5271
5510
|
},
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5511
|
+
takeover_slack: {
|
|
5512
|
+
key: "the time from a heartbeat of the other member to its report of the partition",
|
|
5513
|
+
scope: "broker",
|
|
5514
|
+
unit: "ms",
|
|
5515
|
+
default: 500,
|
|
5516
|
+
note: "an assumption of this document, not a value of Kafka: the reconcile and the next poll"
|
|
5517
|
+
}
|
|
5518
|
+
},
|
|
5519
|
+
timers: {
|
|
5520
|
+
fence: {
|
|
5521
|
+
duration: {
|
|
5522
|
+
expr: "consumer_session_timeout",
|
|
5523
|
+
unit: "ms"
|
|
5524
|
+
},
|
|
5525
|
+
early: {
|
|
5526
|
+
expr: "consumer_heartbeat_interval + early_slack",
|
|
5527
|
+
unit: "ms"
|
|
5528
|
+
},
|
|
5529
|
+
lateness: {
|
|
5530
|
+
expr: "late_slack",
|
|
5531
|
+
unit: "ms"
|
|
5532
|
+
},
|
|
5533
|
+
arms: ["enter:stable"],
|
|
5534
|
+
cancels: ["rx.heartbeat"],
|
|
5535
|
+
fires: "timer.session",
|
|
5536
|
+
note: "from the stop. It runs in the coordinator"
|
|
5537
|
+
},
|
|
5538
|
+
takeover: {
|
|
5539
|
+
duration: {
|
|
5540
|
+
expr: "consumer_session_timeout",
|
|
5541
|
+
unit: "ms"
|
|
5542
|
+
},
|
|
5543
|
+
early: {
|
|
5544
|
+
expr: "consumer_heartbeat_interval + early_slack",
|
|
5545
|
+
unit: "ms"
|
|
5546
|
+
},
|
|
5547
|
+
lateness: {
|
|
5548
|
+
expr: "consumer_heartbeat_interval + late_slack + takeover_slack",
|
|
5549
|
+
unit: "ms"
|
|
5550
|
+
},
|
|
5551
|
+
arms: ["enter:stable"],
|
|
5552
|
+
fires: "timer.takeover",
|
|
5553
|
+
note: "from the stop: the fence, then the next heartbeat of the other member and its next poll"
|
|
5554
|
+
},
|
|
5555
|
+
classic_fence: {
|
|
5556
|
+
duration: {
|
|
5557
|
+
expr: "classic_session_timeout",
|
|
5558
|
+
unit: "ms"
|
|
5559
|
+
},
|
|
5560
|
+
early: {
|
|
5561
|
+
expr: "classic_heartbeat_interval + early_slack",
|
|
5562
|
+
unit: "ms"
|
|
5563
|
+
},
|
|
5564
|
+
lateness: {
|
|
5565
|
+
expr: "late_slack",
|
|
5566
|
+
unit: "ms"
|
|
5567
|
+
},
|
|
5568
|
+
arms: ["enter:stable"],
|
|
5569
|
+
cancels: ["rx.heartbeat"],
|
|
5570
|
+
fires: "timer.session",
|
|
5571
|
+
note: "from the stop, for a classic member. It runs in the coordinator"
|
|
5572
|
+
}
|
|
5573
|
+
},
|
|
5574
|
+
transitions: [
|
|
5575
|
+
{
|
|
5576
|
+
id: "group.heartbeat",
|
|
5577
|
+
from: "stable",
|
|
5578
|
+
to: "stable",
|
|
5579
|
+
on: "rx.heartbeat",
|
|
5580
|
+
note: "the session starts again",
|
|
5581
|
+
source: "GroupMetadataManager.java:2475"
|
|
5582
|
+
},
|
|
5583
|
+
{
|
|
5584
|
+
id: "group.commit",
|
|
5585
|
+
from: "stable",
|
|
5586
|
+
to: "stable",
|
|
5587
|
+
on: "rx.commit",
|
|
5588
|
+
emits: ["reply"],
|
|
5589
|
+
note: "a stop below the session: the epoch is the same, and the commit passes",
|
|
5590
|
+
source: "ConsumerGroup.java:649-690"
|
|
5591
|
+
},
|
|
5592
|
+
{
|
|
5593
|
+
id: "group.fence",
|
|
5594
|
+
from: "stable",
|
|
5595
|
+
to: "fenced",
|
|
5596
|
+
on: "timer.session",
|
|
5597
|
+
emits: ["fence line"],
|
|
5598
|
+
note: "the coordinator removes the member and raises the group epoch",
|
|
5599
|
+
source: "GroupMetadataManager.java:4358, :4687, :4775-4787, :7026"
|
|
5600
|
+
},
|
|
5601
|
+
{
|
|
5602
|
+
id: "group.takeover",
|
|
5603
|
+
from: "fenced",
|
|
5604
|
+
to: "fenced",
|
|
5605
|
+
on: "timer.takeover",
|
|
5606
|
+
note: "the other member owns the partition and reads from the last committed offset",
|
|
5607
|
+
source: "GroupMetadataManager.java:3867, :3900-3947"
|
|
5608
|
+
},
|
|
5609
|
+
{
|
|
5610
|
+
id: "group.commit-fails",
|
|
5611
|
+
from: "fenced",
|
|
5612
|
+
to: "fenced",
|
|
5613
|
+
on: "rx.commit-after-fence",
|
|
5614
|
+
emits: ["UNKNOWN_MEMBER_ID"],
|
|
5615
|
+
note: "the client throws CommitFailedException",
|
|
5616
|
+
source: "ConsumerGroup.java:649-690, CommitRequestManager.java:901-905, ConsumerCoordinator.java:1433-1448"
|
|
5617
|
+
},
|
|
5618
|
+
{
|
|
5619
|
+
id: "group.rejoin",
|
|
5620
|
+
from: "fenced",
|
|
5621
|
+
to: "rejoined",
|
|
5622
|
+
on: "local.poll",
|
|
5623
|
+
note: "the client signals the lost partitions, clears its assignment and joins at epoch 0",
|
|
5624
|
+
source: "AbstractMembershipManager.java:388, ConsumerMembershipManager.java:367-374, AsyncKafkaConsumer.java:1006"
|
|
5625
|
+
},
|
|
5626
|
+
{
|
|
5627
|
+
id: "group.stale-commit",
|
|
5628
|
+
from: "rejoined",
|
|
5629
|
+
to: "rejoined",
|
|
5630
|
+
on: "rx.commit",
|
|
5631
|
+
emits: ["reply"],
|
|
5632
|
+
note: "the check at the same epoch asks no question about who owns the partition, thus the committed offset moves back",
|
|
5633
|
+
source: "ConsumerGroup.java:649-690"
|
|
5634
|
+
}
|
|
5635
|
+
]
|
|
5636
|
+
});
|
|
5637
|
+
//#endregion
|
|
5638
|
+
//#region src/kafka/partition.ts
|
|
5639
|
+
const CORE = "kafka 4.3.2 core/src/main/scala/kafka";
|
|
5640
|
+
const SERVER = "kafka 4.3.2 server/src/main/java/org/apache/kafka/server";
|
|
5641
|
+
const STORAGE = "kafka 4.3.2 storage/src/main/java/org/apache/kafka/storage/internals/log";
|
|
5642
|
+
const CONTROLLER = "kafka 4.3.2 metadata/src/main/java/org/apache/kafka/controller";
|
|
5643
|
+
const RAFT = "kafka 4.3.2 raft/src/main/java/org/apache/kafka/raft";
|
|
5644
|
+
const CLIENTS = "kafka 4.3.2 clients/src/main/java/org/apache/kafka/clients";
|
|
5645
|
+
/**
|
|
5646
|
+
* This document is Apache Kafka 4.3.2, for one broker that leads one partition, in KRaft mode. It
|
|
5647
|
+
* gives what the broker answers at each `acks`, and how the high watermark moves. It also gives how
|
|
5648
|
+
* the broker loses the leadership when a fault cuts it from the other brokers and the controller.
|
|
5649
|
+
*
|
|
5650
|
+
* With `acks=1` a broker answers after its own append. With `acks=all` the write waits in a purgatory
|
|
5651
|
+
* until the high watermark passes it. The high watermark is the smallest log end of the maximal
|
|
5652
|
+
* in-sync replicas. A leader that cannot reach the controller cannot shrink that set, thus its high
|
|
5653
|
+
* watermark stays.
|
|
5654
|
+
*
|
|
5655
|
+
* The controller fences a broker when no heartbeat came for `broker.session.timeout.ms`, and it
|
|
5656
|
+
* elects another leader. The cut broker is not told, and it has no timer that stops it. It stays a
|
|
5657
|
+
* leader in its own view until the metadata of the change reaches it, after the cut lifts. It then
|
|
5658
|
+
* becomes a follower, and it ends each write in the purgatory with NOT_LEADER_OR_FOLLOWER. It fetches
|
|
5659
|
+
* from the new leader, which answers with the end of its log at its election. The old leader
|
|
5660
|
+
* truncates to that offset, not to the high watermark.
|
|
5661
|
+
*/
|
|
5662
|
+
const KAFKA_PARTITION = machineSchema.parse({
|
|
5663
|
+
format: "mark1russell7.machine/1",
|
|
5664
|
+
id: "kafka-partition",
|
|
5665
|
+
title: "Apache Kafka 4.3.2: one broker that leads one partition, its answers at each acks, its high watermark and the loss of its leadership",
|
|
5666
|
+
layer: "server",
|
|
5667
|
+
version: "4.3.2",
|
|
5668
|
+
conditions: {
|
|
5669
|
+
kafka: "4.3.2",
|
|
5670
|
+
mode: "KRaft, with one controller that is not a broker",
|
|
5671
|
+
partition: "one partition with a replica on each of three brokers, unclean leader election off",
|
|
5672
|
+
fault: "the leader is cut from the other brokers and the controller, and the client keeps its route to the leader"
|
|
5673
|
+
},
|
|
5674
|
+
sources: [
|
|
5675
|
+
{
|
|
5676
|
+
ref: `${CORE}/server/ReplicaManager.scala:1360-1366 a delayed produce only when requiredAcks is -1, and :911-915 each other write is answered at once`,
|
|
5677
|
+
rung: "implemented"
|
|
5678
|
+
},
|
|
5679
|
+
{
|
|
5680
|
+
ref: `${CORE}/server/ReplicaManager.scala:885-910 the delayed produce goes in the purgatory`,
|
|
5681
|
+
rung: "implemented"
|
|
5682
|
+
},
|
|
5683
|
+
{
|
|
5684
|
+
ref: `${SERVER}/purgatory/DelayedProduce.java:139-152 it completes on an error, or when each partition has enough replicas`,
|
|
5685
|
+
rung: "implemented"
|
|
5686
|
+
},
|
|
5687
|
+
{
|
|
5688
|
+
ref: `${CORE}/cluster/Partition.scala:947-983 acks=all completes when the high watermark passes the write, with NOT_ENOUGH_REPLICAS_AFTER_APPEND below the minimum, and NOT_LEADER_OR_FOLLOWER off the leader`,
|
|
5689
|
+
rung: "implemented"
|
|
5690
|
+
},
|
|
5691
|
+
{
|
|
5692
|
+
ref: `${CORE}/cluster/Partition.scala:1230-1238 an append with acks=all under min.insync.replicas fails, and each other append goes on`,
|
|
5693
|
+
rung: "implemented"
|
|
5694
|
+
},
|
|
5695
|
+
{
|
|
5696
|
+
ref: `${CORE}/cluster/Partition.scala:1010-1053 the high watermark is the smallest log end of the maximal in-sync replicas, and it does not move under the minimum`,
|
|
5697
|
+
rung: "implemented"
|
|
5698
|
+
},
|
|
5699
|
+
{
|
|
5700
|
+
ref: `${CORE}/cluster/Partition.scala:1089-1127 and :1155-1165 the shrink of the in-sync replicas`,
|
|
5701
|
+
rung: "implemented"
|
|
5702
|
+
},
|
|
5703
|
+
{
|
|
5704
|
+
ref: `${CORE}/cluster/Partition.scala:1636-1659 a shrink is pending until the controller commits it, and ${SERVER}/partition/PendingShrinkIsr.java:36-42 the maximal in-sync replicas stay the committed ones`,
|
|
5705
|
+
rung: "implemented"
|
|
5706
|
+
},
|
|
5707
|
+
{
|
|
5708
|
+
ref: `${SERVER}/config/ReplicationConfigs.java:55 replica.lag.time.max.ms is 30000`,
|
|
5709
|
+
rung: "implemented"
|
|
5710
|
+
},
|
|
5711
|
+
{
|
|
5712
|
+
ref: `${RAFT}/KRaftConfigs.java:40 broker.heartbeat.interval.ms is 2000, and :44 broker.session.timeout.ms is 9000`,
|
|
5713
|
+
rung: "implemented"
|
|
5714
|
+
},
|
|
5715
|
+
{
|
|
5716
|
+
ref: `${CONTROLLER}/BrokerHeartbeatTracker.java:99-121 and :131-133 a broker expires when no heartbeat came in the session timeout`,
|
|
5717
|
+
rung: "implemented"
|
|
5718
|
+
},
|
|
5719
|
+
{
|
|
5720
|
+
ref: `${CONTROLLER}/QuorumController.java:1659-1660 the controller looks for one stale broker each max(1 ms, session / 8), and :1672-1675 repeats`,
|
|
5721
|
+
rung: "implemented"
|
|
5722
|
+
},
|
|
5723
|
+
{
|
|
5724
|
+
ref: `${CONTROLLER}/ReplicationControlManager.java:1769-1797 and :2110-2112 it fences the broker and moves each of its leaderships, and PartitionChangeBuilder.java:256-267 takes the first valid replica in replica order`,
|
|
5725
|
+
rung: "implemented"
|
|
5726
|
+
},
|
|
5727
|
+
{
|
|
5728
|
+
ref: `${SERVER}/BrokerLifecycleManager.java:601-604 and :619-624 a broker that gets no heartbeat reply schedules the next heartbeat, and it stops nothing`,
|
|
5729
|
+
rung: "implemented"
|
|
5730
|
+
},
|
|
5731
|
+
{
|
|
5732
|
+
ref: `${RAFT}/QuorumConfig.java:83 the fetch timeout is 2000, and :104 the retry backoff is 20`,
|
|
5733
|
+
rung: "implemented"
|
|
5734
|
+
},
|
|
5735
|
+
{
|
|
5736
|
+
ref: `${CORE}/raft/KafkaRaftManager.scala:236-237 the reconnect backoff of the raft client is 50 to 500 ms, and :249-251 its setup timeouts are the settings of the broker`,
|
|
5737
|
+
rung: "implemented"
|
|
5738
|
+
},
|
|
5739
|
+
{
|
|
5740
|
+
ref: `${CLIENTS}/ClusterConnectionStates.java:41 and :43 a backoff and a setup timeout take a jitter of 0.2, and :372-379 a setup that fails doubles the timeout`,
|
|
5741
|
+
rung: "implemented"
|
|
5742
|
+
},
|
|
5743
|
+
{
|
|
5744
|
+
ref: `kafka 4.3.2 server-common/src/main/java/org/apache/kafka/server/config/ServerConfigs.java:71-77 socket.connection.setup.timeout.ms on a broker`,
|
|
5745
|
+
rung: "implemented"
|
|
5746
|
+
},
|
|
5747
|
+
{
|
|
5748
|
+
ref: `${CLIENTS}/CommonClientConfigs.java:139 and :144 socket.connection.setup.timeout.ms is 10000, and its maximum is 30000`,
|
|
5749
|
+
rung: "implemented"
|
|
5750
|
+
},
|
|
5751
|
+
{
|
|
5752
|
+
ref: `${CORE}/server/ReplicaManager.scala:2448-2544 the delta of a follower: makeFollower at :2472, a new fetcher at :2535, and each delayed write ends at :2539`,
|
|
5753
|
+
rung: "implemented"
|
|
5754
|
+
},
|
|
5755
|
+
{
|
|
5756
|
+
ref: `${CORE}/cluster/Partition.scala:1383-1413 a leader answers a fetch of an older epoch with the diverging epoch, and ${CORE}/server/AbstractFetcherThread.scala:603-641 the follower truncates to the end of that epoch`,
|
|
5757
|
+
rung: "implemented"
|
|
5758
|
+
},
|
|
5759
|
+
{
|
|
5760
|
+
ref: `${CORE}/cluster/Partition.scala:635-649 makeLeader, and :694-743 makeFollower`,
|
|
5761
|
+
rung: "implemented"
|
|
5762
|
+
},
|
|
5763
|
+
{
|
|
5764
|
+
ref: `${STORAGE}/UnifiedLog.java:1411-1421 and :1261-1268 an idempotent batch that repeats is not appended, and the answer gives its first offset`,
|
|
5765
|
+
rung: "implemented"
|
|
5766
|
+
},
|
|
5767
|
+
{
|
|
5768
|
+
ref: `${STORAGE}/ProducerStateEntry.java:35 and :128-135 the broker keeps the last 5 batches of each producer, and a repeat has their exact range of sequence numbers`,
|
|
5769
|
+
rung: "implemented"
|
|
5770
|
+
}
|
|
5771
|
+
],
|
|
5772
|
+
states: {
|
|
5773
|
+
leader: {
|
|
5774
|
+
initial: true,
|
|
5775
|
+
note: "the controller and the broker agree that the broker leads"
|
|
5776
|
+
},
|
|
5777
|
+
deposed: { note: "the controller moved the leadership, and the broker does not know. It appends each write. It answers acks=1, and acks=all waits, because no fetch reaches it and its high watermark stays" },
|
|
5778
|
+
follower: { note: "the broker fetches from the new leader" }
|
|
5779
|
+
},
|
|
5780
|
+
events: {
|
|
5781
|
+
"rx.produce-1": {
|
|
5782
|
+
kind: "rx",
|
|
5783
|
+
note: "a write with acks=1"
|
|
5784
|
+
},
|
|
5785
|
+
"rx.produce-all": {
|
|
5786
|
+
kind: "rx",
|
|
5787
|
+
note: "a write with acks=all, with enough in-sync replicas"
|
|
5788
|
+
},
|
|
5789
|
+
"rx.produce-all-short": {
|
|
5790
|
+
kind: "rx",
|
|
5791
|
+
note: "a write with acks=all, under min.insync.replicas"
|
|
5792
|
+
},
|
|
5793
|
+
"rx.produce-repeat": {
|
|
5794
|
+
kind: "rx",
|
|
5795
|
+
note: "an idempotent batch with the sequence numbers of one of the last 5 batches of its producer"
|
|
5796
|
+
},
|
|
5797
|
+
"rx.fetch": {
|
|
5798
|
+
kind: "rx",
|
|
5799
|
+
note: "a fetch of a follower"
|
|
5800
|
+
},
|
|
5801
|
+
"rx.heartbeat": {
|
|
5802
|
+
kind: "rx",
|
|
5803
|
+
note: "a heartbeat of this broker reaches the controller"
|
|
5804
|
+
},
|
|
5805
|
+
"local.lift": {
|
|
5806
|
+
kind: "local",
|
|
5807
|
+
note: "the cut lifts"
|
|
5808
|
+
},
|
|
5809
|
+
"timer.lag": {
|
|
5810
|
+
kind: "timer",
|
|
5811
|
+
note: "a follower did not catch up in replica.lag.time.max.ms"
|
|
5812
|
+
},
|
|
5813
|
+
"timer.fence": {
|
|
5814
|
+
kind: "timer",
|
|
5815
|
+
note: "the controller finds the session of the broker expired"
|
|
5816
|
+
},
|
|
5817
|
+
"timer.metadata": {
|
|
5818
|
+
kind: "timer",
|
|
5819
|
+
note: "the metadata of the change reaches the broker"
|
|
5820
|
+
},
|
|
5821
|
+
"rx.diverging-epoch": {
|
|
5822
|
+
kind: "rx",
|
|
5823
|
+
note: "the new leader answers a fetch with the end of the old epoch"
|
|
5824
|
+
},
|
|
5825
|
+
"rx.elected": {
|
|
5826
|
+
kind: "rx",
|
|
5827
|
+
note: "the metadata names this broker as the leader again"
|
|
5828
|
+
}
|
|
5829
|
+
},
|
|
5830
|
+
knobs: {
|
|
5831
|
+
broker_heartbeat_interval: {
|
|
5832
|
+
key: "broker.heartbeat.interval.ms",
|
|
5833
|
+
scope: "broker",
|
|
5834
|
+
unit: "ms",
|
|
5835
|
+
default: 2e3,
|
|
5836
|
+
note: "KRaftConfigs.java:40"
|
|
5837
|
+
},
|
|
5838
|
+
broker_session_timeout: {
|
|
5839
|
+
key: "broker.session.timeout.ms",
|
|
5840
|
+
scope: "broker",
|
|
5841
|
+
unit: "ms",
|
|
5842
|
+
default: 9e3,
|
|
5843
|
+
note: "KRaftConfigs.java:44"
|
|
5844
|
+
},
|
|
5845
|
+
replica_lag_time_max: {
|
|
5846
|
+
key: "replica.lag.time.max.ms",
|
|
5847
|
+
scope: "broker",
|
|
5848
|
+
unit: "ms",
|
|
5849
|
+
default: 3e4,
|
|
5850
|
+
note: "ReplicationConfigs.java:55"
|
|
5851
|
+
},
|
|
5852
|
+
socket_connection_setup_timeout: {
|
|
5853
|
+
key: "socket.connection.setup.timeout.ms",
|
|
5854
|
+
scope: "broker",
|
|
5855
|
+
unit: "ms",
|
|
5856
|
+
default: 1e4,
|
|
5857
|
+
note: "CommonClientConfigs.java:139"
|
|
5858
|
+
},
|
|
5859
|
+
raft_reconnect_backoff_max: {
|
|
5860
|
+
key: "the largest reconnect backoff of the raft client",
|
|
5861
|
+
scope: "build",
|
|
5862
|
+
unit: "ms",
|
|
5863
|
+
default: 500,
|
|
5864
|
+
note: "KafkaRaftManager.scala:237"
|
|
5865
|
+
},
|
|
5866
|
+
connection_jitter: {
|
|
5867
|
+
key: "the jitter of a setup timeout and of a backoff",
|
|
5868
|
+
scope: "build",
|
|
5869
|
+
unit: "count",
|
|
5870
|
+
default: .2,
|
|
5871
|
+
note: "ClusterConnectionStates.java:41, :43"
|
|
5872
|
+
},
|
|
5873
|
+
slack: {
|
|
5874
|
+
key: "the round trips and the steps from the first connect to the publish of the metadata",
|
|
5875
|
+
scope: "broker",
|
|
5876
|
+
unit: "ms",
|
|
5877
|
+
default: 100,
|
|
5878
|
+
note: "an assumption of this document, not a value of Kafka: the version request, a fetch, and the publish of the batch"
|
|
5879
|
+
}
|
|
5880
|
+
},
|
|
5881
|
+
timers: {
|
|
5882
|
+
replica_lag: {
|
|
5883
|
+
duration: {
|
|
5884
|
+
expr: "replica_lag_time_max",
|
|
5885
|
+
unit: "ms"
|
|
5886
|
+
},
|
|
5887
|
+
arms: ["enter:leader"],
|
|
5888
|
+
cancels: ["rx.fetch"],
|
|
5889
|
+
fires: "timer.lag",
|
|
5890
|
+
note: "the first follower that lags. The broker asks the controller to shrink, and a cut broker gets no answer"
|
|
5891
|
+
},
|
|
5892
|
+
fence: {
|
|
5893
|
+
duration: {
|
|
5894
|
+
expr: "broker_session_timeout",
|
|
5895
|
+
unit: "ms"
|
|
5896
|
+
},
|
|
5897
|
+
early: {
|
|
5898
|
+
expr: "broker_heartbeat_interval",
|
|
5899
|
+
unit: "ms"
|
|
5900
|
+
},
|
|
5901
|
+
lateness: {
|
|
5902
|
+
expr: "max(1, broker_session_timeout / 8)",
|
|
5903
|
+
unit: "ms"
|
|
5904
|
+
},
|
|
5905
|
+
arms: ["enter:leader"],
|
|
5906
|
+
cancels: ["rx.heartbeat"],
|
|
5907
|
+
fires: "timer.fence",
|
|
5908
|
+
note: "from the fault. It runs in the controller"
|
|
5909
|
+
},
|
|
5910
|
+
step_down: {
|
|
5911
|
+
duration: {
|
|
5912
|
+
const: 0,
|
|
5913
|
+
unit: "ms"
|
|
5914
|
+
},
|
|
5915
|
+
lateness: {
|
|
5916
|
+
expr: "socket_connection_setup_timeout * (1 + connection_jitter) + raft_reconnect_backoff_max * (1 + connection_jitter) + slack",
|
|
5917
|
+
unit: "ms"
|
|
5918
|
+
},
|
|
5919
|
+
arms: ["local.lift"],
|
|
5920
|
+
fires: "timer.metadata",
|
|
5921
|
+
note: "from the lift. No timer of Kafka: the longest wait of the raft client for its next fetch from the controller. A hold above the fetch timeout has no fetch in flight at the lift"
|
|
5922
|
+
}
|
|
5923
|
+
},
|
|
5924
|
+
transitions: [
|
|
5925
|
+
{
|
|
5926
|
+
id: "kafka.append-1",
|
|
5927
|
+
from: "leader",
|
|
5928
|
+
to: "leader",
|
|
5929
|
+
on: "rx.produce-1",
|
|
5930
|
+
emits: ["reply"],
|
|
5931
|
+
note: "the reply follows the local append",
|
|
5932
|
+
source: "ReplicaManager.scala:911-915, :1360-1366"
|
|
5933
|
+
},
|
|
5934
|
+
{
|
|
5935
|
+
id: "kafka.append-all",
|
|
5936
|
+
from: "leader",
|
|
5937
|
+
to: "leader",
|
|
5938
|
+
on: "rx.produce-all",
|
|
5939
|
+
note: "the write waits in the purgatory",
|
|
5940
|
+
source: "ReplicaManager.scala:885-910, DelayedProduce.java:139-152"
|
|
5941
|
+
},
|
|
5942
|
+
{
|
|
5943
|
+
id: "kafka.refuse",
|
|
5944
|
+
from: "leader",
|
|
5945
|
+
to: "leader",
|
|
5946
|
+
on: "rx.produce-all-short",
|
|
5947
|
+
emits: ["NOT_ENOUGH_REPLICAS"],
|
|
5948
|
+
source: "Partition.scala:1230-1238"
|
|
5949
|
+
},
|
|
5950
|
+
{
|
|
5951
|
+
id: "kafka.repeat",
|
|
5952
|
+
from: "leader",
|
|
5953
|
+
to: "leader",
|
|
5954
|
+
on: "rx.produce-repeat",
|
|
5955
|
+
emits: ["reply"],
|
|
5956
|
+
note: "the log does not change, and the reply gives the first offset",
|
|
5957
|
+
source: "UnifiedLog.java:1411-1421, ProducerStateEntry.java:128-135"
|
|
5958
|
+
},
|
|
5959
|
+
{
|
|
5960
|
+
id: "kafka.fetch",
|
|
5961
|
+
from: "leader",
|
|
5962
|
+
to: "leader",
|
|
5963
|
+
on: "rx.fetch",
|
|
5964
|
+
emits: ["reply"],
|
|
5965
|
+
note: "the high watermark moves, and each write that it passes completes",
|
|
5966
|
+
source: "Partition.scala:947-983, :1010-1053"
|
|
5967
|
+
},
|
|
5968
|
+
{
|
|
5969
|
+
id: "kafka.heartbeat",
|
|
5970
|
+
from: "leader",
|
|
5971
|
+
to: "leader",
|
|
5972
|
+
on: "rx.heartbeat",
|
|
5973
|
+
source: "BrokerHeartbeatTracker.java:99-121"
|
|
5974
|
+
},
|
|
5975
|
+
{
|
|
5976
|
+
id: "kafka.lift-early",
|
|
5977
|
+
from: "leader",
|
|
5978
|
+
to: "leader",
|
|
5979
|
+
on: "local.lift",
|
|
5980
|
+
note: "a hold that ends before the fence: the heartbeats go on, and the leadership stays",
|
|
5981
|
+
source: "BrokerLifecycleManager.java:601-604"
|
|
5982
|
+
},
|
|
5983
|
+
{
|
|
5984
|
+
id: "kafka.shrink-pending",
|
|
5985
|
+
from: "leader",
|
|
5986
|
+
to: "leader",
|
|
5987
|
+
on: "timer.lag",
|
|
5988
|
+
note: "the shrink stays pending: the maximal in-sync replicas keep each follower",
|
|
5989
|
+
source: "Partition.scala:1636-1659, PendingShrinkIsr.java:36-42"
|
|
5990
|
+
},
|
|
5991
|
+
{
|
|
5992
|
+
id: "kafka.fenced",
|
|
5993
|
+
from: "leader",
|
|
5994
|
+
to: "deposed",
|
|
5995
|
+
on: "timer.fence",
|
|
5996
|
+
note: "the first valid replica in replica order leads, with the log end that it has",
|
|
5997
|
+
source: "BrokerHeartbeatTracker.java:131-133, QuorumController.java:1659-1660, ReplicationControlManager.java:1769-1797, PartitionChangeBuilder.java:256-267"
|
|
5998
|
+
},
|
|
5999
|
+
{
|
|
6000
|
+
id: "kafka.deposed-append-1",
|
|
6001
|
+
from: "deposed",
|
|
6002
|
+
to: "deposed",
|
|
6003
|
+
on: "rx.produce-1",
|
|
6004
|
+
emits: ["reply"],
|
|
6005
|
+
note: "the broker has no check of its own fence",
|
|
6006
|
+
source: "ReplicaManager.scala:911-915, BrokerLifecycleManager.java:619-624"
|
|
6007
|
+
},
|
|
6008
|
+
{
|
|
6009
|
+
id: "kafka.deposed-append-all",
|
|
6010
|
+
from: "deposed",
|
|
6011
|
+
to: "deposed",
|
|
6012
|
+
on: "rx.produce-all",
|
|
6013
|
+
note: "the write waits: the high watermark stays",
|
|
6014
|
+
source: "Partition.scala:969, :1028-1032"
|
|
6015
|
+
},
|
|
6016
|
+
{
|
|
6017
|
+
id: "kafka.deposed-shrink",
|
|
6018
|
+
from: "deposed",
|
|
6019
|
+
to: "deposed",
|
|
6020
|
+
on: "timer.lag",
|
|
6021
|
+
note: "the shrink stays pending",
|
|
6022
|
+
source: "Partition.scala:1636-1659"
|
|
6023
|
+
},
|
|
6024
|
+
{
|
|
6025
|
+
id: "kafka.deposed-lift",
|
|
6026
|
+
from: "deposed",
|
|
6027
|
+
to: "deposed",
|
|
6028
|
+
on: "local.lift",
|
|
6029
|
+
note: "the wait for the metadata starts",
|
|
6030
|
+
source: "KafkaRaftManager.scala:236-237, :249-251"
|
|
6031
|
+
},
|
|
6032
|
+
{
|
|
6033
|
+
id: "kafka.step-down",
|
|
6034
|
+
from: "deposed",
|
|
6035
|
+
to: "follower",
|
|
6036
|
+
on: "timer.metadata",
|
|
6037
|
+
emits: ["NOT_LEADER_OR_FOLLOWER"],
|
|
6038
|
+
note: "each write in the purgatory ends, and the producer sends it to the new leader",
|
|
6039
|
+
source: "ReplicaManager.scala:2448-2544, Partition.scala:694-743"
|
|
6040
|
+
},
|
|
6041
|
+
{
|
|
6042
|
+
id: "kafka.truncate",
|
|
6043
|
+
from: "follower",
|
|
6044
|
+
to: "follower",
|
|
6045
|
+
on: "rx.diverging-epoch",
|
|
6046
|
+
note: "the log ends at the end of the new leader's log at its election",
|
|
6047
|
+
source: "Partition.scala:1383-1413, AbstractFetcherThread.scala:603-641"
|
|
6048
|
+
},
|
|
6049
|
+
{
|
|
6050
|
+
id: "kafka.elected",
|
|
6051
|
+
from: "follower",
|
|
6052
|
+
to: "leader",
|
|
6053
|
+
on: "rx.elected",
|
|
6054
|
+
source: "Partition.scala:635-649"
|
|
6055
|
+
}
|
|
6056
|
+
]
|
|
6057
|
+
});
|
|
6058
|
+
//#endregion
|
|
6059
|
+
//#region src/mongo/mongod-replication.ts
|
|
6060
|
+
const AT$13 = "mongodb r9.0.4 src/mongo/db/repl";
|
|
6061
|
+
/**
|
|
6062
|
+
* This document is the replication of mongod 9.0.4. It gives one member of a replica set, its view
|
|
6063
|
+
* of the primary, and the election that it can hold. The states are the states of the member. The
|
|
6064
|
+
* timers are the heartbeats, the election timeout, the liveness check of a primary, the vote, the
|
|
6065
|
+
* catch-up of a new primary and a step-down.
|
|
6066
|
+
*
|
|
6067
|
+
* Each member sends a heartbeat to each other member. A heartbeat window is 10 s: in it, a failed
|
|
6068
|
+
* request goes again at once, two times at most. A window without a response marks the target
|
|
6069
|
+
* DOWN. A secondary starts an election when it hears nothing from the primary for the election
|
|
6070
|
+
* timeout and a random offset. A primary steps down when it cannot see a majority for the election
|
|
6071
|
+
* timeout.
|
|
6072
|
+
*
|
|
6073
|
+
* A secondary keeps a silent primary as its sync source until its oplog fetcher gives up. That is
|
|
6074
|
+
* two timeouts and a restart: 17 s to 18 s at the defaults. Its reporter keeps a
|
|
6075
|
+
* replSetUpdatePosition in flight to that primary for 30 s. Until then, a new primary learns the
|
|
6076
|
+
* position of that secondary only from the response to each heartbeat.
|
|
6077
|
+
*/
|
|
6078
|
+
const MONGOD_REPL = machineSchema.parse({
|
|
6079
|
+
format: "mark1russell7.machine/1",
|
|
6080
|
+
id: "mongod-replication",
|
|
6081
|
+
title: "mongod 9.0.4 replication: one member of a replica set, its heartbeats, its elections and its step-downs",
|
|
6082
|
+
layer: "server",
|
|
6083
|
+
version: "9.0.4",
|
|
6084
|
+
conditions: {
|
|
6085
|
+
mongodb: "9.0.4",
|
|
6086
|
+
protocol: "replication protocol version 1",
|
|
6087
|
+
members: "three members, each with one vote and priority 1"
|
|
6088
|
+
},
|
|
6089
|
+
sources: [
|
|
6090
|
+
{
|
|
6091
|
+
ref: `${AT$13}/repl_set_config.idl:74-109 the settings: heartbeatIntervalMillis 2000, heartbeatTimeoutSecs 10, electionTimeoutMillis 10000, catchUpTimeoutMillis -1, catchUpTakeoverDelayMillis 30000`,
|
|
6092
|
+
rung: "implemented"
|
|
6093
|
+
},
|
|
6094
|
+
{
|
|
6095
|
+
ref: `${AT$13}/repl_server_parameters.idl:320-328 replElectionTimeoutOffsetLimitFraction 0.15`,
|
|
6096
|
+
rung: "implemented"
|
|
6097
|
+
},
|
|
6098
|
+
{
|
|
6099
|
+
ref: `${AT$13}/topology_coordinator.cpp:1058-1101 the heartbeat window and the timeout of each request, :1124-1147 the next heartbeat, :1239-1248 a member DOWN`,
|
|
6100
|
+
rung: "implemented"
|
|
6101
|
+
},
|
|
6102
|
+
{
|
|
6103
|
+
ref: `${AT$13}/topology_coordinator.h:62 kMaxHeartbeatRetries 2`,
|
|
6104
|
+
rung: "implemented"
|
|
6105
|
+
},
|
|
6106
|
+
{
|
|
6107
|
+
ref: `${AT$13}/replication_coordinator_impl_heartbeat.cpp:97-111 the random offset, :346-352 a heartbeat from the primary postpones the election, :1215-1271 the election timeout and its offset`,
|
|
6108
|
+
rung: "implemented"
|
|
6109
|
+
},
|
|
6110
|
+
{
|
|
6111
|
+
ref: `${AT$13}/data_replicator_external_state_impl.cpp:70-81 an oplog batch that names a primary postpones the election`,
|
|
6112
|
+
rung: "implemented"
|
|
6113
|
+
},
|
|
6114
|
+
{
|
|
6115
|
+
ref: `${AT$13}/delayable_timeout_callback.cpp:149-172 the offset changes at most once in its own length`,
|
|
6116
|
+
rung: "implemented"
|
|
6117
|
+
},
|
|
6118
|
+
{
|
|
6119
|
+
ref: `${AT$13}/topology_coordinator.cpp:1440-1457 and :1476-1489 the liveness check: a primary that cannot see a majority steps down`,
|
|
6120
|
+
rung: "implemented"
|
|
6121
|
+
},
|
|
6122
|
+
{
|
|
6123
|
+
ref: `${AT$13}/replication_coordinator_impl_heartbeat.cpp:1158-1188 the liveness check runs at the stalest member's last update plus the election timeout`,
|
|
6124
|
+
rung: "implemented"
|
|
6125
|
+
},
|
|
6126
|
+
{
|
|
6127
|
+
ref: `${AT$13}/replication_coordinator_impl_elect_v1.cpp:189-226 the dry run, :268-382 the term, the durable vote and the real vote, :408-468 the win`,
|
|
6128
|
+
rung: "implemented"
|
|
6129
|
+
},
|
|
6130
|
+
{
|
|
6131
|
+
ref: `${AT$13}/vote_requester.cpp:96 a vote request times out at min(electionTimeoutMillis, 30 s), :160-177 a majority of yes votes ends the round`,
|
|
6132
|
+
rung: "implemented"
|
|
6133
|
+
},
|
|
6134
|
+
{
|
|
6135
|
+
ref: `${AT$13}/topology_coordinator.cpp:3741-3790 a member votes for each candidate whose data is not staler than its own, and only an arbiter refuses one because it sees a primary`,
|
|
6136
|
+
rung: "implemented"
|
|
6137
|
+
},
|
|
6138
|
+
{
|
|
6139
|
+
ref: `${AT$13}/replication_coordinator_impl_catchup.cpp:35-85 the catch-up and its timeout, :110-133 it ends when each other member answered a heartbeat since the win, or failed one, :349-379 the win restarts each heartbeat`,
|
|
6140
|
+
rung: "implemented"
|
|
6141
|
+
},
|
|
6142
|
+
{
|
|
6143
|
+
ref: `${AT$13}/topology_coordinator.cpp:3841-3849 and :3910-3932 a restarted heartbeat, and the latest optime that the catch-up waits for`,
|
|
6144
|
+
rung: "implemented"
|
|
6145
|
+
},
|
|
6146
|
+
{
|
|
6147
|
+
ref: `${AT$13}/replication_coordinator_impl.cpp:1490-1570 the end of the drain: a reconfig of the term, then writes, :2580-2588 the hello of a primary that cannot write yet`,
|
|
6148
|
+
rung: "implemented"
|
|
6149
|
+
},
|
|
6150
|
+
{
|
|
6151
|
+
ref: `${AT$13}/replication_coordinator_impl_step_up_step_down.cpp:64-329 replSetStepDown: the wait for a caught-up secondary, the step-down and the hand-off`,
|
|
6152
|
+
rung: "implemented"
|
|
6153
|
+
},
|
|
6154
|
+
{
|
|
6155
|
+
ref: `${AT$13}/repl_set_commands.cpp:545-561 stepDownSecs 60 and secondaryCatchUpPeriodSecs 10`,
|
|
6156
|
+
rung: "implemented"
|
|
6157
|
+
},
|
|
6158
|
+
{
|
|
6159
|
+
ref: `${AT$13}/replication_coordinator_impl.idl:18-26 enableElectionHandoff true, and replication_coordinator_impl.cpp:2830-2873 the hand-off: replSetStepUp with skipDryRun`,
|
|
6160
|
+
rung: "implemented"
|
|
6161
|
+
},
|
|
6162
|
+
{
|
|
6163
|
+
ref: `${AT$13}/replication_coordinator_impl.cpp:1516 the reconfig of the term, :4598 and :4750 a new config starts each heartbeat again`,
|
|
6164
|
+
rung: "implemented"
|
|
6165
|
+
},
|
|
6166
|
+
{
|
|
6167
|
+
ref: `${AT$13}/replication_coordinator_impl_heartbeat.cpp:367-376 a heartbeat response that moves a member's optime forward moves the commit point and wakes the writes that wait`,
|
|
6168
|
+
rung: "implemented"
|
|
6169
|
+
},
|
|
6170
|
+
{
|
|
6171
|
+
ref: `${AT$13}/oplog_fetcher.cpp:119-133 the awaitData timeout min(electionTimeoutMillis / 2, 30 s), :317-324 the socket timeout adds oplogNetworkTimeoutBufferSeconds, :640-647 the exhaust read`,
|
|
6172
|
+
rung: "implemented"
|
|
6173
|
+
},
|
|
6174
|
+
{
|
|
6175
|
+
ref: `${AT$13}/oplog_fetcher.cpp:395-441 an error makes a new cursor while restarts remain, :596-611 the new cursor sets the retried find timeout, :1073-1113 one restart after 5 ms`,
|
|
6176
|
+
rung: "implemented"
|
|
6177
|
+
},
|
|
6178
|
+
{
|
|
6179
|
+
ref: `${AT$13}/repl_server_parameters.idl:121-126 oplogRetriedFindMaxSeconds 2, :234-244 oplogFetcherSteadyStateMaxFetcherRestarts 1, :344-351 oplogNetworkTimeoutBufferSeconds 5`,
|
|
6180
|
+
rung: "implemented"
|
|
6181
|
+
},
|
|
6182
|
+
{
|
|
6183
|
+
ref: "mongodb r9.0.4 src/mongo/transport/asio/asio_session_impl.cpp:500-527 the timeout of a synchronous session is SO_RCVTIMEO, and src/mongo/client/dbclient_session.cpp:345-349 its connect uses the same timeout",
|
|
6184
|
+
rung: "implemented"
|
|
6185
|
+
},
|
|
6186
|
+
{
|
|
6187
|
+
ref: `${AT$13}/bgsync.cpp:395-447 a member without a sync source waits for new heartbeat data or 1 s (:971-976), and topology_coordinator.cpp:1124-1136 it sends its heartbeats each 500 ms`,
|
|
6188
|
+
rung: "implemented"
|
|
6189
|
+
},
|
|
6190
|
+
{
|
|
6191
|
+
ref: `${AT$13}/replication_coordinator_impl.cpp:4947-4972 a member that loses its sync source starts each heartbeat again at once`,
|
|
6192
|
+
rung: "implemented"
|
|
6193
|
+
},
|
|
6194
|
+
{
|
|
6195
|
+
ref: `${AT$13}/sync_source_feedback.cpp:37-47 the keepalive of the reporter min(electionTimeoutMillis / 2, 30 s), :39-40 the network timeout 30 s, :99-117 and :134-236 a new reporter only after the old one ends`,
|
|
6196
|
+
rung: "implemented"
|
|
6197
|
+
},
|
|
6198
|
+
{
|
|
6199
|
+
ref: `${AT$13}/reporter.cpp:113-117 a reporter ends when its command in flight ends, :119-169 a trigger, :195-228 each replSetUpdatePosition has the network timeout`,
|
|
6200
|
+
rung: "implemented"
|
|
6201
|
+
},
|
|
6202
|
+
{
|
|
6203
|
+
ref: "linux v6.6 net/core/sock.c:427-460 SO_RCVTIMEO in jiffies, and kernel/time/timer.c:516-528 the wheel rounds a timer up to the granularity of its level",
|
|
6204
|
+
rung: "implemented"
|
|
6205
|
+
}
|
|
6206
|
+
],
|
|
6207
|
+
states: {
|
|
6208
|
+
startup: {
|
|
6209
|
+
initial: true,
|
|
6210
|
+
pseudo: true
|
|
6211
|
+
},
|
|
6212
|
+
secondary: { note: "SECONDARY: it replicates from its sync source, and its election timeout runs" },
|
|
6213
|
+
"dry-run": { note: "a dry run: votes are asked in the current term (replication_coordinator_impl_elect_v1.cpp:200-226)" },
|
|
6214
|
+
candidate: { note: "the real election: a new term, a durable vote for itself, and a vote request to each member" },
|
|
6215
|
+
catchup: { note: "PRIMARY in catch-up: it waits for a heartbeat answer from each other member, or a failure (log 21359)" },
|
|
6216
|
+
drain: { note: "PRIMARY in drain mode: the applier ends its buffer, and the hello says secondary (replication_coordinator_impl.cpp:2580-2584)" },
|
|
6217
|
+
primary: { note: "PRIMARY that accepts writes (log 21331)" },
|
|
6218
|
+
"stepping-down": { note: "replSetStepDown waits for a caught-up secondary, and the hello says not writable (step_up_step_down.cpp:46-52)" }
|
|
6219
|
+
},
|
|
6220
|
+
events: {
|
|
6221
|
+
"tx.heartbeat": {
|
|
6222
|
+
kind: "tx",
|
|
6223
|
+
note: "a heartbeat request, the first of a window or a retry in it"
|
|
6224
|
+
},
|
|
6225
|
+
"rx.heartbeat": {
|
|
6226
|
+
kind: "rx",
|
|
6227
|
+
note: "a heartbeat response"
|
|
6228
|
+
},
|
|
6229
|
+
"rx.primary-heartbeat": {
|
|
6230
|
+
kind: "rx",
|
|
6231
|
+
note: "a heartbeat response from the primary in the current term"
|
|
6232
|
+
},
|
|
6233
|
+
"rx.primary-oplog": {
|
|
6234
|
+
kind: "rx",
|
|
6235
|
+
note: "an oplog batch whose metadata names a primary"
|
|
6236
|
+
},
|
|
6237
|
+
"rx.member": {
|
|
6238
|
+
kind: "rx",
|
|
6239
|
+
note: "any message from a member: a heartbeat request or response, or an update of its position"
|
|
6240
|
+
},
|
|
6241
|
+
"timer.heartbeat": { kind: "timer" },
|
|
6242
|
+
"timer.heartbeat-window": {
|
|
6243
|
+
kind: "timer",
|
|
6244
|
+
note: "the window ends without a response: the target is DOWN (log 21216)"
|
|
6245
|
+
},
|
|
6246
|
+
"timer.election": {
|
|
6247
|
+
kind: "timer",
|
|
6248
|
+
note: "log 4615652: Starting an election"
|
|
6249
|
+
},
|
|
6250
|
+
"timer.liveness": {
|
|
6251
|
+
kind: "timer",
|
|
6252
|
+
note: "log 21809: Can't see a majority of the set, relinquishing primary"
|
|
6253
|
+
},
|
|
6254
|
+
"rx.votes": {
|
|
6255
|
+
kind: "rx",
|
|
6256
|
+
note: "a majority of yes votes"
|
|
6257
|
+
},
|
|
6258
|
+
"rx.no-votes": {
|
|
6259
|
+
kind: "rx",
|
|
6260
|
+
note: "the round ends without a majority"
|
|
6261
|
+
},
|
|
6262
|
+
"local.caught-up": {
|
|
6263
|
+
kind: "local",
|
|
6264
|
+
note: "log 21364 or 21368"
|
|
6265
|
+
},
|
|
6266
|
+
"timer.catchup": { kind: "timer" },
|
|
6267
|
+
"local.drained": {
|
|
6268
|
+
kind: "local",
|
|
6269
|
+
note: "log 21331: Transition to primary complete"
|
|
6270
|
+
},
|
|
6271
|
+
"rx.higher-term": { kind: "rx" },
|
|
6272
|
+
"app.step-down": {
|
|
6273
|
+
kind: "app",
|
|
6274
|
+
note: "replSetStepDown"
|
|
6275
|
+
},
|
|
6276
|
+
"timer.step-down-wait": { kind: "timer" },
|
|
6277
|
+
"local.secondary-caught-up": { kind: "local" },
|
|
6278
|
+
"app.step-up": {
|
|
5275
6279
|
kind: "app",
|
|
5276
6280
|
note: "replSetStepUp, as a hand-off sends it with skipDryRun"
|
|
5277
6281
|
},
|
|
@@ -9307,6 +10311,18 @@ const PGBOUNCER_SERVER = machineSchema.parse({
|
|
|
9307
10311
|
ref: `${PB} src/objects.c:1876-1897 launch_new_connection: one connect of a pool at a time, and none for server_login_retry from the launch of a failed one`,
|
|
9308
10312
|
rung: "implemented"
|
|
9309
10313
|
},
|
|
10314
|
+
{
|
|
10315
|
+
ref: `${PB} src/server.c:294-303 pool_pool_size: the pool_size of the user, else the pool_size of the database, else default_pool_size`,
|
|
10316
|
+
rung: "implemented"
|
|
10317
|
+
},
|
|
10318
|
+
{
|
|
10319
|
+
ref: `${PB} src/objects.c:1927-1955 launch_new_connection: a pool with default_pool_size servers starts no more, and the client stays in the queue. reserve_pool_size is 0 at the defaults (src/main.c:317)`,
|
|
10320
|
+
rung: "implemented"
|
|
10321
|
+
},
|
|
10322
|
+
{
|
|
10323
|
+
ref: `${PB} src/janitor.c:166-169 a waiting client with no idle server starts one server. Thus the pool opens each server only when a client needs it, and min_pool_size is 0 at the defaults (src/main.c:308)`,
|
|
10324
|
+
rung: "implemented"
|
|
10325
|
+
},
|
|
9310
10326
|
{
|
|
9311
10327
|
ref: `${PB} src/objects.c:1990-2008 the launch sets the time of the connect, before the DNS lookup and the TCP connect`,
|
|
9312
10328
|
rung: "implemented"
|
|
@@ -9489,6 +10505,13 @@ const PGBOUNCER_SERVER = machineSchema.parse({
|
|
|
9489
10505
|
}
|
|
9490
10506
|
},
|
|
9491
10507
|
knobs: {
|
|
10508
|
+
default_pool_size: {
|
|
10509
|
+
key: "pgbouncer default_pool_size",
|
|
10510
|
+
scope: "proxy",
|
|
10511
|
+
unit: "count",
|
|
10512
|
+
default: 20,
|
|
10513
|
+
note: "src/main.c:284. The most servers of a pool, for one database and one user (src/server.c:294-303, src/objects.c:1927-1955)"
|
|
10514
|
+
},
|
|
9492
10515
|
server_connect_timeout: {
|
|
9493
10516
|
key: "pgbouncer server_connect_timeout",
|
|
9494
10517
|
scope: "proxy",
|
|
@@ -9650,9 +10673,9 @@ const PGBOUNCER_SERVER = machineSchema.parse({
|
|
|
9650
10673
|
from: "[none]",
|
|
9651
10674
|
to: "connecting",
|
|
9652
10675
|
on: "local.launch",
|
|
9653
|
-
guard: "no connect of the pool is in progress",
|
|
10676
|
+
guard: "a client waits, no connect of the pool is in progress, and the pool has fewer servers than default_pool_size",
|
|
9654
10677
|
timer: "connect_timeout",
|
|
9655
|
-
source: "src/objects.c:1876-1887, :1990-2008"
|
|
10678
|
+
source: "src/janitor.c:166-169, src/objects.c:1876-1887, :1927-1955, :1990-2008"
|
|
9656
10679
|
},
|
|
9657
10680
|
{
|
|
9658
10681
|
id: "pbs.connected",
|
|
@@ -18120,6 +19143,8 @@ const MACHINES = byId([
|
|
|
18120
19143
|
RABBITMQ_CONNECTION,
|
|
18121
19144
|
ATEN_DETECTOR,
|
|
18122
19145
|
RA_MEMBER,
|
|
19146
|
+
KAFKA_CONSUMER_GROUP,
|
|
19147
|
+
KAFKA_PARTITION,
|
|
18123
19148
|
GRPC_JS,
|
|
18124
19149
|
GRPC_GO_SERVER,
|
|
18125
19150
|
ETCD_SERVER,
|
|
@@ -18133,9 +19158,11 @@ const MACHINES = byId([
|
|
|
18133
19158
|
ISTIO_SIDECAR_OUTBOUND,
|
|
18134
19159
|
ISTIO_SIDECAR_INBOUND,
|
|
18135
19160
|
PILOT_AGENT,
|
|
18136
|
-
ZTUNNEL_HBONE
|
|
19161
|
+
ZTUNNEL_HBONE,
|
|
19162
|
+
DEADLINE_CALL,
|
|
19163
|
+
KUBELET_READINESS
|
|
18137
19164
|
]);
|
|
18138
19165
|
//#endregion
|
|
18139
|
-
export { AMQPLIB, ATEN_DETECTOR, CADDY_HTTP2, DOCKER_PROXY_LINK, ENVOY_HTTP2, ETCD_SERVER, GO_TRANSPORT, GRPC_GO_SERVER, GRPC_JS, HAPROXY_TCP, HIKARICP_PGJDBC, IOREDIS_SENTINEL, IOREDIS_STATUS, ISTIO_SIDECAR_INBOUND, ISTIO_SIDECAR_OUTBOUND, LINUX_TCP, MACHINES, MANIFESTATIONS, MONGODB_NODE, MONGOD_REPL, MONGOD_SESSION, NF_CONNTRACK_TCP, NGINX_GRPC, NODE_HTTP_AGENT, NODE_HTTP_SERVER, NODE_POSTGRES, NODE_TIMER_EARLY, PGBOUNCER_CLIENT, PGBOUNCER_SERVER, PGPOOL_SESSION, PILOT_AGENT, POSTGRES_SESSION, PROCEDURES_WS_CLIENT, PROCEDURES_WS_SERVER, RABBITMQ_CONNECTION, RA_MEMBER, REDIS_CLIENT, SENTINEL, SUPAVISOR_CLIENT, SUPAVISOR_SERVER, TOXIPROXY_LINK, TRAEFIK_HTTP2, UNDICI_CLIENT, UNDICI_RETRY, UNDICI_TICK_MS, ZTUNNEL_HBONE, goLateSeconds, goTimerLateness, istioSidecar, nodeEarlySeconds, nodeLateSeconds, nodeTimerLateness, undiciFastEarly, undiciFastLateness };
|
|
19166
|
+
export { AMQPLIB, ATEN_DETECTOR, CADDY_HTTP2, DEADLINE_CALL, DOCKER_PROXY_LINK, ENVOY_HTTP2, ETCD_SERVER, GO_TRANSPORT, GRPC_GO_SERVER, GRPC_JS, HAPROXY_TCP, HIKARICP_PGJDBC, IOREDIS_SENTINEL, IOREDIS_STATUS, ISTIO_SIDECAR_INBOUND, ISTIO_SIDECAR_OUTBOUND, KAFKA_CONSUMER_GROUP, KAFKA_PARTITION, KUBELET_READINESS, LINUX_TCP, MACHINES, MANIFESTATIONS, MONGODB_NODE, MONGOD_REPL, MONGOD_SESSION, NF_CONNTRACK_TCP, NGINX_GRPC, NODE_HTTP_AGENT, NODE_HTTP_SERVER, NODE_POSTGRES, NODE_TIMER_EARLY, PGBOUNCER_CLIENT, PGBOUNCER_SERVER, PGPOOL_SESSION, PILOT_AGENT, POSTGRES_SESSION, PROCEDURES_WS_CLIENT, PROCEDURES_WS_SERVER, RABBITMQ_CONNECTION, RA_MEMBER, REDIS_CLIENT, SENTINEL, SUPAVISOR_CLIENT, SUPAVISOR_SERVER, TOXIPROXY_LINK, TRAEFIK_HTTP2, UNDICI_CLIENT, UNDICI_RETRY, UNDICI_TICK_MS, ZTUNNEL_HBONE, goLateSeconds, goTimerLateness, istioSidecar, nodeEarlySeconds, nodeLateSeconds, nodeTimerLateness, undiciFastEarly, undiciFastLateness };
|
|
18140
19167
|
|
|
18141
19168
|
//# sourceMappingURL=index.js.map
|