Documentation
¶
Overview ¶
Package micro provides distributed shard management functionality for the ECS framework. It implements a leader-follower model with deterministic replay for maintaining consistency across distributed game instances.
Index ¶
- Constants
- Variables
- func Endpoint(s *ServiceAddress, endpoint string) string
- func RegisterCommand[T ShardCommand](s *Shard) error
- func String(s *ServiceAddress) string
- type Channel
- type Client
- type ClientOption
- type Command
- type Handler
- type JetStreamSnapshotStorage
- type JetStreamSnapshotStorageOptions
- type NATSConfig
- type NopSnapshotStorage
- type Realm
- type Request
- type Response
- type Service
- type ServiceAddress
- type ServiceEndpointGroup
- type ServiceID
- type Shard
- type ShardCommand
- type ShardEngine
- type ShardMode
- type ShardOptions
- type Snapshot
- type SnapshotStorage
- type SnapshotStorageOptions
- type SnapshotStorageType
- type Tick
- type TickData
- type TickHeader
Constants ¶
const ( RealmUnspecified = microv1.ServiceAddress_REALM_UNSPECIFIED // Should not be used RealmInternal = microv1.ServiceAddress_REALM_INTERNAL // Reserved for internal services RealmWorld = microv1.ServiceAddress_REALM_WORLD // Publicly accessible services that are )
const MinEpochFrequency = 10
Variables ¶
var (
ErrEndpointAlreadyExists = eris.New("endpoint already exists")
)
var (
ErrInvalidAddress = eris.New("invalid service address format")
)
Functions ¶
func Endpoint ¶
func Endpoint(s *ServiceAddress, endpoint string) string
Endpoint returns the full service address including the endpoint.
func RegisterCommand ¶
func RegisterCommand[T ShardCommand](s *Shard) error
RegisterCommand registers a command type T with the shard's command manager. This allows the shard to receive and process commands of the specified type.
func String ¶
func String(s *ServiceAddress) string
String returns the string representation of a ServiceAddress without the endpoint.
Types ¶
type Channel ¶
type Channel[T ShardCommand] chan Command
Channel is a generic buffered channel for handling commands of a specific type. It implements the commandChannel interface and provides type-safe command processing.
type Client ¶
Client represents a NATS client with enhanced logging and error handling.
func NewClient ¶
func NewClient(opts ...ClientOption) (*Client, error)
NewClient creates a new NATS client with the given configuration. It handles connection setup, error handling, and logging.
func NewTestClient ¶
NewTestClient creates a NATS client specifically for testing without authentication. This should only be used in test environments with unauthenticated NATS servers.
func (*Client) Close ¶
func (c *Client) Close()
Close gracefully closes the NATS connection and logs the event.
func (*Client) Request ¶
func (c *Client) Request( ctx context.Context, address *ServiceAddress, endpoint string, payload proto.Message, ) (*microv1.Response, error)
Request sends a request to a subject and waits for a response (request-reply pattern). The timeout should be set in ctx.
func (*Client) RequestAndSubscribe ¶ added in v0.6.0
func (c *Client) RequestAndSubscribe( ctx context.Context, sendAddress *ServiceAddress, sendEndpoint string, receiveAddress *ServiceAddress, receiveEndpoint string, payload proto.Message, ) (*nats.Msg, error)
RequestAndSubscribe sends a message to the send endpoint and waits for a message on the receive endpoint. This is useful when the response comes on a different subject than the request (e.g., events). The timeout should be set in ctx.
type ClientOption ¶
type ClientOption func(*Client)
ClientOption defines a function that can modify a Client.
func WithLogger ¶
func WithLogger(log zerolog.Logger) ClientOption
WithLogger returns a ClientOption that sets the logger.
func WithNATSConfig ¶
func WithNATSConfig(cfg NATSConfig) ClientOption
WithNATSConfig returns a ClientOption that sets the NATS configuration.
type Command ¶
type Command struct {
Name string // The command name
Address *ServiceAddress // Service address this command is sent to
Persona string // Sender's persona
Payload any // The command payload itself
}
Command represents a command from a player or external system.
type Handler ¶
Handler defines the signature for all service endpoint handlers. It guarantees that all responses follow the protobuf schema defined in response.proto.
type JetStreamSnapshotStorage ¶
type JetStreamSnapshotStorage struct {
// contains filtered or unexported fields
}
JetStreamSnapshotStorage implements SnapshotStorage using NATS JetStream ObjectStore.
func NewJetStreamSnapshotStorage ¶
func NewJetStreamSnapshotStorage(opts JetStreamSnapshotStorageOptions) (*JetStreamSnapshotStorage, error)
NewJetStreamSnapshotStorage creates a new JetStream ObjectStore-based snapshot storage. It creates its own NATS client using the default configuration from environment variables.
func (*JetStreamSnapshotStorage) Exists ¶
func (j *JetStreamSnapshotStorage) Exists() bool
func (*JetStreamSnapshotStorage) Load ¶
func (j *JetStreamSnapshotStorage) Load() (*Snapshot, error)
func (*JetStreamSnapshotStorage) Store ¶
func (j *JetStreamSnapshotStorage) Store(snapshot *Snapshot) error
type JetStreamSnapshotStorageOptions ¶
type JetStreamSnapshotStorageOptions struct {
ObjectName string `env:"SHARD_SNAPSHOT_JETSTREAM_OBJECT_NAME" envDefault:"snapshot"`
// Maximum bytes for snapshot storage (ObjectStore). // Required by some NATS providers like Synadia Cloud.
SnapshotStorageMaxBytes uint64 `env:"SHARD_SNAPSHOT_STORAGE_MAX_BYTES" envDefault:"0"`
// contains filtered or unexported fields
}
type NATSConfig ¶
type NATSConfig struct {
Name string `env:"NATS_NAME" envDefault:"isc"`
URL string `env:"NATS_URL" envDefault:"nats://nats:4222"`
CredentialsFile string `env:"NATS_CREDENTIALS_FILE"`
}
NATSConfig holds the configuration for the NATS client.
func (NATSConfig) Validate ¶
func (cfg NATSConfig) Validate() error
Validate validates the NATS configuration and returns an error if invalid.
type NopSnapshotStorage ¶
type NopSnapshotStorage struct{}
NopSnapshotStorage is a no-op implementation of SnapshotStorage. It's used when snapshots are not needed (e.g., development, testing).
func NewNopSnapshotStorage ¶
func NewNopSnapshotStorage() *NopSnapshotStorage
NewNopSnapshotStorage creates a new no-op snapshot storage.
func (*NopSnapshotStorage) Exists ¶
func (n *NopSnapshotStorage) Exists() bool
func (*NopSnapshotStorage) Load ¶
func (n *NopSnapshotStorage) Load() (*Snapshot, error)
func (*NopSnapshotStorage) Store ¶
func (n *NopSnapshotStorage) Store(_ *Snapshot) error
type Realm ¶
type Realm = microv1.ServiceAddress_Realm
Realm represents the access scope of the service.
type Request ¶
type Request struct {
// Raw is the original NATS message.
Raw *nats.Msg
// RequestID is extracted from the request if available.
RequestID string
// ServiceAddress contains the service address information.
ServiceAddress *microv1.ServiceAddress
// Payload contains the decoded request payload, if any.
Payload *anypb.Any
}
Request represents an incoming service request with its metadata.
func NewRequestFromNATSMsg ¶ added in v0.7.0
NewRequestFromNATSMsg converts a nats.Msg to a Request, parsing the payload if present.
type Response ¶
type Response struct {
// RequestID is copied from the request.
RequestID string
// ServiceAddress contains the service address information.
ServiceAddress *microv1.ServiceAddress
// Status contains the response status information.
Status *spb.Status
// Payload contains the response payload, if any.
Payload *anypb.Any
}
Response represents a structured response that follows the protobuf schema.
func NewErrorResponse ¶
NewErrorResponse creates an error response with the given error. The code parameter must not be codes.OK, as this function is only for error responses.
func NewSuccessResponse ¶
NewSuccessResponse creates a successful response with optional payload.
type Service ¶
type Service struct {
Address *ServiceAddress // This service's address
Version string // Version represents the service version.
// contains filtered or unexported fields
}
Service represents a micro service that can serve requests.
func NewService ¶
func NewService(client *Client, address *ServiceAddress, tel *telemetry.Telemetry) (*Service, error)
NewService creates a new service with the given NATS client, service address, and telemetry.
func (*Service) AddEndpoint ¶
AddEndpoint adds an endpoint to the service. The endpoint will be registered under the service's address as a prefix.
Example:
svc.AddEndpoint("ping", handlePing) // -> "<service_address>.ping"
func (*Service) AddGroup ¶
func (s *Service) AddGroup(name string) *ServiceEndpointGroup
AddGroup returns a helper struct that allows registering a group of endpoints with a common prefix. For example, all endpoints in the "message" group will be registered as "message.<endpoint_name>".
Example:
messageGroup := svc.AddGroup("message")
messageGroup.AddEndpoint("send", handleMessageSend) // -> "<service_address>.message.send"
messageGroup.AddEndpoint("delete", handleMessageDelete) // -> "<service_address>.message.delete"
messageGroup.AddEndpoint("update", handleMessageUpdate) // -> "<service_address>.message.update"
type ServiceAddress ¶
type ServiceAddress = microv1.ServiceAddress
ServiceAddress is an alias to the protobuf-generated ServiceAddress type.
func GetAddress ¶
func GetAddress(region string, realm Realm, organization, project string, serviceID ServiceID) *ServiceAddress
GetAddress creates a new ServiceAddress with the given realm, organization, project, and service ID.
func ParseAddress ¶
func ParseAddress(address string) (*ServiceAddress, error)
ParseAddress parses a string into a ServiceAddress. The format should be "<region>.<realm>.<organization>.<project>.<service_id>".
type ServiceEndpointGroup ¶
type ServiceEndpointGroup struct {
// contains filtered or unexported fields
}
ServiceEndpointGroup is a helper struct that allows registering a group of endpoints with a common prefix.
func (*ServiceEndpointGroup) AddEndpoint ¶
func (g *ServiceEndpointGroup) AddEndpoint(name string, handler Handler) error
type Shard ¶
type Shard struct {
// contains filtered or unexported fields
}
Shard manages the shard lifecycle depending on the operation mode.
func NewShard ¶
func NewShard(base ShardEngine, opts ShardOptions) (*Shard, error)
NewShard creates a new shard instance with the given base implementation and options.
func (*Shard) Base ¶
func (s *Shard) Base() ShardEngine
Base returns the underlying shard engine implementation.
func (*Shard) CurrentTick ¶
CurrentTick returns the current tick information. Returns an error if called during the inter-tick period, whic happens after an epoch is published but before the next tick is started.
func (*Shard) IsDisablePersona ¶
type ShardCommand ¶
type ShardCommand interface {
Name() string
}
type ShardEngine ¶
type ShardEngine interface {
// Init initializes the shard's initial state.
Init() error
// Tick processes the given input and advances the shard state.
Tick(Tick) error
// Replay processes the given input during state reconstruction.
// This should produce the same result as Tick for deterministic replay.
Replay(Tick) error
// StateHash returns a hash of the current shard state for consistency verification.
StateHash() ([]byte, error)
// Snapshot captures the current shard state and returns it as serialized bytes.
Snapshot() ([]byte, error)
// Restore restores the shard state from the given serialized bytes.
Restore(data []byte) error
// Reset resets the engine to its clean initial state.
Reset()
}
ShardEngine defines the interface that must be implemented by specific shard types. It provides the core functionality for initializing, processing inputs, and maintaining state.
type ShardOptions ¶
type ShardOptions struct {
Client *Client // NATS client
Address *ServiceAddress // Shard's service address
Mode ShardMode // Operation mode (Leader or Follower)
EpochFrequency uint32 // Number of ticks per epoch
TickRate float64 // Number of ticks per second
Telemetry *telemetry.Telemetry // Telemetry for logging and tracing
SnapshotStorageType SnapshotStorageType // Snapshot storage type
SnapshotStorageOptions SnapshotStorageOptions // Optional snapshot storage options
SnapshotFrequency uint32 // Number of epochs per snapshot
DisablePersona bool // Disable persona verification for development/testing
EpochStreamMaxBytes uint32 // Maximum bytes for epoch stream (required by some NATS providers)
}
ShardOptions contains configuration options for creating a new shard.
type Snapshot ¶
Snapshot represents a point-in-time capture of shard state. This is an alias to the protobuf-generated type for better API ergonomics.
type SnapshotStorage ¶
type SnapshotStorage interface {
// Store saves the snapshot, atomically replacing any existing snapshot.
// The previous snapshot should be preserved as backup if possible.
Store(snapshot *Snapshot) error
// Load retrieves the current snapshot.
// Returns an error if no snapshot exists.
Load() (*Snapshot, error)
// Exists checks if a current snapshot is available.
Exists() bool
}
SnapshotStorage provides persistence for shard snapshots. Implementations handle atomic storage with automatic backup of previous snapshots.
type SnapshotStorageOptions ¶
type SnapshotStorageOptions interface {
// contains filtered or unexported methods
}
type SnapshotStorageType ¶
type SnapshotStorageType uint8
SnapshotStorageType defines the type of snapshot storage to use.
const ( SnapshotStorageUndefined SnapshotStorageType = iota SnapshotStorageNop SnapshotStorageJetStream )
func (SnapshotStorageType) String ¶
func (s SnapshotStorageType) String() string
type Tick ¶
type Tick struct {
Header TickHeader // Metadata about when and which tick this represents
Data TickData // The actual commands processed during this tick
}
Tick represents a single execution step in the shard's lifecycle.
type TickData ¶
type TickData struct {
Commands []Command // List of commands executed in this tick
}
TickData contains the commands that were processed during this tick execution.
type TickHeader ¶
type TickHeader struct {
TickHeight uint64 // Tick height
Timestamp time.Time // When this tick was created
}
TickHeader contains metadata about the tick execution.