protocol

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 10, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package protocol implements the monolock binary wire format.

All multi-byte integers are big endian. TCP is a byte stream, so every fixed size field is read with io.ReadFull.

Client -> server:

ACQUIRE   [0x01][version uint8][lease_ms uint32][name_len uint16][name bytes]
HEARTBEAT [0x02]

Server -> client:

WAITING   [0x11]
ACQUIRED  [0x12][token uint64]
ERROR     [0x13][code uint8][reason_len uint8][reason bytes]

The lease is chosen by the client: a session that stays quiet for lease_ms is dropped by the server. It must be positive and stays constant for the lifetime of the connection.

The token is a fencing token: it grows monotonically with every grant of any lock, so the resource a lock guards can reject writes carrying a token smaller than one it has already seen. Every ACQUIRED of a session carries the same token the session was granted.

Index

Constants

View Source
const (
	MsgAcquire   uint8 = 0x01
	MsgHeartbeat uint8 = 0x02
)

Client -> server message types.

View Source
const (
	MsgWaiting  uint8 = 0x11
	MsgAcquired uint8 = 0x12
	MsgError    uint8 = 0x13
)

Server -> client message types.

View Source
const MaxNameLen = 255

MaxNameLen is the largest accepted lock name, in bytes. It is a protocol constant rather than a knob: client and server enforce the same value, so a name a client can build is a name the server will accept. The wire format carries a uint16 length and can express more, which the server rejects.

View Source
const Version uint8 = 1

Version is the protocol version implemented here.

Variables

View Source
var (
	ErrShuttingDown       = newError(0x01, "server shutting down")
	ErrAdminClosed        = newError(0x02, "closed by administrator")
	ErrUnsupportedVersion = newError(0x10, "unsupported protocol version")
	ErrExpectedAcquire    = newError(0x11, "first message must be ACQUIRE")
	ErrDuplicateAcquire   = newError(0x12, "duplicate ACQUIRE")
	ErrUnknownMessage     = newError(0x13, "unknown message type")
	ErrEmptyName          = newError(0x14, "empty lock name")
	ErrNameTooLong        = newError(0x15, "lock name too long")
	ErrNameNotUTF8        = newError(0x16, "lock name is not valid UTF-8")
	ErrInvalidLease       = newError(0x17, "lease must be positive")
	ErrNotAuthorized      = newError(0x18, "not authorized for lock")
)

Canonical protocol errors, one wire code each. AppendAcquire and ReadAcquireBody return the name and lease ones when validating input; the server sends the code and text of an ERROR message from the same values, so a client can match a received code back to one of them with CodeErr.

Functions

func AppendAcquire

func AppendAcquire(dst []byte, name string, leaseMS uint32) ([]byte, error)

AppendAcquire appends an ACQUIRE message for name with the given lease to dst.

func AppendAcquired

func AppendAcquired(dst []byte, token uint64) []byte

AppendAcquired appends an ACQUIRED message carrying the fencing token.

func AppendError

func AppendError(dst []byte, code uint8, reason string) []byte

AppendError appends an ERROR message with the given code and reason. The reason is for humans: clients branch on the code alone. A reason longer than 255 bytes is truncated to fit the length field.

func ErrCodeName

func ErrCodeName(c uint8) string

ErrCodeName returns a human readable name for an ERROR code.

func MsgName

func MsgName(t uint8) string

MsgName returns a human readable name for a message type.

func ReadAcquireBody

func ReadAcquireBody(r io.Reader) (version uint8, leaseMS uint32, name string, err error)

ReadAcquireBody reads an ACQUIRE message body, i.e. everything after the type byte, which the caller has already consumed.

A name longer than MaxNameLen fails without reading the name itself; the caller is expected to answer with ERROR and close the connection, so the stream is not resynchronised.

func ReadAcquiredBody

func ReadAcquiredBody(r io.Reader) (token uint64, err error)

ReadAcquiredBody reads an ACQUIRED message body, i.e. everything after the type byte, which the caller has already consumed.

func ReadErrorBody

func ReadErrorBody(r io.Reader) (code uint8, reason string, err error)

ReadErrorBody reads an ERROR message body, i.e. everything after the type byte, which the caller has already consumed.

Types

type Error

type Error struct {
	Code uint8
	// contains filtered or unexported fields
}

Error is a protocol error: a stable wire code plus its canonical text. The code is what clients branch on, the text is for humans.

func CodeErr

func CodeErr(c uint8) *Error

CodeErr returns the canonical Error for a wire code, or nil for a code this version of the protocol does not know.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Temporary

func (e *Error) Temporary() bool

Temporary reports whether reconnecting may help. Codes below 0x10 are server conditions; everything above is a client error and terminal, since the same bytes will fail the same way. The split is a range so that a client classifies codes it does not know yet the same way.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL