필드 제약과 값 범위
요청 필드 (C→S)
| 필드 | 타입 | 유효 범위 | 의미 | 위반 시 |
|---|---|---|---|---|
op | ASCII 1B | A(0x41) · R(0x52) | 요청 종류 | E bad_op |
wait | u8 | 0 ~ 255 (초) | 0은 queue 없는 즉시 시도, 1..255는 획득 대기 상한 | — (타입상 항상 유효) |
lease | u8 | 1 ~ 250 (초) | 임대. 0, 251..255는 거부/reserved | E bad_lease |
owner | u64 BE | 0 ~ 2^64-1 전체 | 획득 시도 식별자 (클라이언트 생성) | — (서버가 검사하지 않음) |
token | u64 BE | 서버가 발급한 값 | 반납 대상 지정 (R 전용) | 불일치 시 N |
key | UTF-8 | 1 ~ 128 바이트 | 락 이름. 스페이스(0x20)·개행(0x0A) 불가 | E bad_key |
- 무한 wait/lease는 없습니다. 공식 클라이언트는 초 미만 값을 올림한 뒤 범위를 검사하며, 범위를 넘으면 clamp하거나 전송하지 않고 명시적 입력 오류를 반환합니다. 기본 lease는 30초입니다.
- 공식 client API의
wait는 호출 시작부터 local queue, 미송신 연결 재시도, write와 응답까지를 포함한 전체 acquire 상한입니다. monotonic operation deadline을 한 번만 만들고 writer가 실제 송신 직전에 남은 시간을 u8 초로 올림해 frame의wait에 넣습니다. 따라서 연결이 늦게 살아나거나 확정 미송신 failover가 일어나도 server wait가 처음부터 다시 시작되지 않습니다. deadline 당시 아직 queue에 있으면 확정 미송신 timeout, 한 바이트라도 보냈을 수 있으면 정확한 해당 session을 닫고Indeterminate입니다. 원래wait=0만 wire0을 사용하며, 이 즉시 시도에도 멈춘 transport를 무한히 기다리지 않는 별도의 5초 I/O deadline이 있습니다. 그 안에서 확정 미송신이면 같은 owner로 재시도할 수 있습니다. 이는 server-side 시도가 아직 일어나지 않은 것이므로 server queue 대기를 허용하는 뜻이 아니며, 한 바이트라도 송신됐을 가능성이 생기면 재전송하지 않습니다. 단, deadline 직전 이미 상관된A를 Ticket 전달 gate에서 늦게 확인한 경우에는 server waiter가 해소됐으므로 session을 닫지 않고 known token을 보상 release한 뒤Indeterminate로 끝냅니다. 이미 상관된T/B는 확정 미획득입니다. - 공식 client API는 wire에 없는
min_work_budget을 필수로 받습니다.critical work + 예상 pause + DB commit/rollback 종료의 최소 합이며0..250초, 정규화된 lease 이하여야 합니다. 위반하면 frame을 보내기 전에UnsupportedDuration/InsufficientLease계열 오류를 반환합니다. - 키 길이는 글자 수가 아니라 바이트 수입니다 — 한글은 UTF-8에서 글자당 3바이트이므로 최대 42글자입니다.
owner는 응답 상관 ID일 뿐 권한이 아닙니다. 값이 겹쳐도 active token을 반환하거나 lease를 갱신하지 않습니다. 다만 한 클라이언트의 동시에 진행 중인 요청끼리는 응답 매칭이 모호하지 않게 서로 다른 1 이상의 값을 사용해야 합니다. 공식 클라이언트는 local counter가 0이 되거나 wrap하면 새 acquire를 fail-closed합니다.bad_key는 빈 키, 128바이트 초과, 스페이스·개행 포함, 유효하지 않은 UTF-8 모두에 해당합니다. 공식 클라이언트는 여기에 더해\r도 보내지 않습니다 —\r로 끝나는 키는 눈에 안 보이는 채로 다른 키가 되기 때문입니다.- 공식 클라이언트의 explicit release와 token을 이미 아는 보상 release는 호출·enqueue 시점부터 절대 5초 안에서만 재접속·재시도합니다. 이 deadline은 lease 길이까지 연장되지 않습니다.
R을 확인하면 성공,N이면 이미 사라졌거나 현재 token이 아님으로 구분하고, 응답을 확인하지 못하면 성공으로 추정하지 않습니다. 보상 queue가 가득 차면 안전한 최후 수단인 lease expiry에 맡기며 queue를 무제한으로 키우지 않습니다.
응답 필드 (S→C)
| 필드 | 타입 | 범위 | 나타나는 응답 |
|---|---|---|---|
token | u64 BE | 1 이상, 발급마다 증가 | A(신규 발급) · R/N(요청 에코) |
owner | u64 BE | 요청 값 그대로 | A · T · B (요청 에코) |
key | UTF-8 | 요청 값 그대로 (1~128B) | A · T · B · R · N |
addr | UTF-8 | host:port | M · L |
reason | ASCII | 정해진 8가지 | E |
토큰은 0을 발급하지 않습니다. 싱글은 현재 process lifetime의 counter, 클러스터는 합의로 복제된 counter를 사용하며 overflow 직전에는 새 Grant를 fail-closed합니다. wall clock이나 난수로 재시작 단조성을 주장하지 않습니다.
고정 헤더 길이
op 뒤에 오는 고정 길이 이진 구간입니다. 이 바이트들은 0x0A를 포함할 수 있으므로 파서는 개행을 찾기 전에 이 길이만큼 먼저 소비해야 합니다.
| 방향 | op | 고정 헤더 | 구성 |
|---|---|---|---|
| C→S | A | 10 B | wait(1) + lease(1) + owner(8) |
| C→S | R | 8 B | token(8) |
| S→C | A | 16 B | token(8) + owner(8) |
| S→C | T · B | 8 B | owner(8) |
| S→C | R · N | 8 B | token(8) |
| S→C | M · L · E | 0 B | 없음 (op 뒤 바로 텍스트) |
바이트가 부족하면 E bad_request입니다.
프레임 크기
| 항목 | 값 |
|---|---|
프레임 상한 (\n 제외) | 192 바이트 — 초과 시 E line_too_long |
최대 A 요청 | 1 + 10 + 128 + 1 = 140 B |
최대 R 요청 | 1 + 8 + 128 + 1 = 138 B |
최대 A 응답 | 1 + 16 + 128 + 1 = 146 B |
빈 프레임 (\n 하나) | keep-alive — 서버가 무시 |
상한 192B는 최대 프레임(146B)보다 넉넉해서, 규격을 지키는 클라이언트는 절대 걸리지 않습니다. 구조적으로 잘못됐거나 oversized인 frame 뒤에는 framing을 더 신뢰하지 않고 연결을 닫습니다.
인증 핸드셰이크
| 항목 | 값 |
|---|---|
| 해시 | SHA-256 (challenge 라인에 명시) |
| nonce | base64url 문자 8자 |
| 응답 다이제스트 | base64url 43자 (패딩 없음) |
| 라인 상한 | 256 바이트 |
| 제한 시간 | 10초 (TLS 협상 포함) — 초과 시 연결 종료 |
자세한 절차는 인증 핸드셰이크를 참고하세요.
서버 측 한계
클라이언트가 직접 지정하지는 않지만 동작에 영향을 주는 값들입니다.
| 항목 | 기본값 | 설정 | 초과 시 |
|---|---|---|---|
| 키당 대기자 수 | 2048 (절대 상한 16384) | MAX_WAITERS | B(사용 중) |
| 전체 대기자 수 | 16384 (절대 상한 65536) | MAX_TOTAL_WAITERS | 해당 acquire에 B |
| 동시 클라이언트 연결 | 1024 (절대 상한 8192) | MAX_CONNECTIONS | 연결 즉시 종료 |
| 연결당 밀린 응답 | 256 | (컴파일 상수) | 연결 종료 — 응답을 읽지 않는 클라이언트로 판단 |
| 전체 in-flight acquire | 4096 | (컴파일 상수) | 해당 acquire에 B |
| 전체 in-flight release | 512 별도 lane | (컴파일 상수) | connection 종료 전까지 bounded 대기 |
| cluster active key | 65536 | (컴파일 상수) | 신규 key acquire에 B |
읽기 버퍼(4096B), 응답 배치(64개), 시작된 client frame의 progress timeout(5초), single 만료 청소 주기(5초)는 환경변수가 아니라 컴파일 타임 상수입니다. cluster 만료는 전체 scan이 아니라 token을 재검증하는 deadline min-heap으로 처리합니다. 설정 가능한 값의 전체 목록과 유효 범위는 설정 (환경변수)에 있습니다.
전체 acquire·신규 key·전체 waiter admission은 각 hard limit의 90%에서 닫히고 사용량이 75% 미만으로 내려온 뒤 다시 열립니다. 따라서 표의 hard limit에 닿기 전에도 신규 acquire가 B를 받을 수 있으며, release와 Raft 복구용 예약 용량은 이 hysteresis와 분리됩니다.
위반 → 응답 요약
| 상황 | 응답 | 연결 |
|---|---|---|
| 알 수 없는 op | E bad_op | 닫힘 |
| 고정 헤더 부족 | E bad_request | 닫힘 |
lease = 0 또는 251..255 | E bad_lease | 닫힘 |
| 키가 빔 / 128B 초과 / 스페이스·개행 포함 / UTF-8 아님 | E bad_key | 닫힘 |
| 프레임 192B 초과 | E line_too_long | 닫힘 |
| 인증 다이제스트 불일치 | E auth_failed | 닫힘 |
| 대기열/서버 capacity 포화 | B + owner + key | 유지 |
| 반납 토큰 불일치 | N + token + key | 유지 |
전체 오류 사유와 연결 처리는 응답 · 오류 E를 참고하세요.