service

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jan 28, 2026 License: MIT Imports: 16 Imported by: 0

README

Common Service Framework

Shared service infrastructure for all Neo Service Layer marble services.

Overview

The infrastructure/service package provides a consistent foundation for all marble services with:

  • Standardized lifecycle management (Start/Stop)
  • Background worker registration and management
  • Hydration hooks for state loading
  • Standard HTTP endpoints (/health, /ready, /info)
  • Statistics provider interface

File Structure

File Purpose
base.go BaseService implementation
interfaces.go Service interfaces and contracts
routes.go Standard HTTP handlers and routes

Core Components

BaseService

The foundation for all marble services.

type BaseService struct {
    *marble.Service

    // Lifecycle management
    stopCh   chan struct{}
    stopOnce sync.Once

    // Extensibility hooks
    hydrate func(context.Context) error
    statsFn func() map[string]any

    // Worker management
    workers []func(context.Context)
}
BaseConfig

Configuration structure for creating a BaseService.

type BaseConfig struct {
    ID      string
    Name    string
    Version string
    Marble  *marble.Marble
    DB      database.RepositoryInterface
}

Service Interfaces

MarbleService (Required)

All marble services must implement this interface:

type MarbleService interface {
    // Identity
    ID() string
    Name() string
    Version() string

    // Lifecycle
    Start(ctx context.Context) error
    Stop() error

    // HTTP
    Router() *mux.Router
}
StatisticsProvider (Optional)

Services can provide runtime statistics:

type StatisticsProvider interface {
    Statistics() map[string]any
}
Hydratable (Optional)

Services can reload state from persistence:

type Hydratable interface {
    Hydrate(ctx context.Context) error
}
ChainIntegrated (Optional)

Services that interact with blockchain:

type ChainIntegrated interface {
    ChainClient() *chain.Client
}
HealthChecker (Optional)

Services with custom health status:

type HealthChecker interface {
    HealthStatus() string              // "healthy", "degraded", "unhealthy"
    HealthDetails() map[string]any
}

Usage

Creating a Service
package myservice

import (
    commonservice "github.com/R3E-Network/service_layer/infrastructure/service"
)

type Service struct {
    *commonservice.BaseService
    // ... service-specific fields
}

func New(cfg Config) (*Service, error) {
    base := commonservice.NewBase(&commonservice.BaseConfig{
        ID:      "myservice",
        Name:    "My Service",
        Version: "1.0.0",
        Marble:  cfg.Marble,
        DB:      cfg.DB,
    })

    s := &Service{
        BaseService: base,
    }

    // Register hydration hook
    base.WithHydrate(s.loadState)

    // Register statistics provider
    base.WithStats(s.getStatistics)

    // Register background workers
    base.AddWorker(s.runBackgroundTask)
    base.AddTickerWorker(time.Minute, s.runPeriodicTask)

    // Register standard routes
    base.RegisterStandardRoutes()

    // Register service-specific routes
    s.registerRoutes()

    return s, nil
}
Adding Background Workers
// Simple worker (runs once, manages its own loop)
base.AddWorker(func(ctx context.Context) {
    ticker := time.NewTicker(time.Minute)
    defer ticker.Stop()

    for {
        select {
        case <-ctx.Done():
            return
        case <-base.StopChan():
            return
        case <-ticker.C:
            // Do work
        }
    }
})

// Ticker worker (convenience for periodic tasks)
base.AddTickerWorker(time.Minute, func(ctx context.Context) error {
    // Called every minute
    return nil
})
Providing Statistics
base.WithStats(func() map[string]any {
    return map[string]any{
        "active_jobs":   s.countActiveJobs(),
        "total_requests": s.totalRequests,
        "uptime":        time.Since(s.startTime).String(),
    }
})

Standard HTTP Endpoints

GET /health

Returns service health status.

Response:

{
    "status": "healthy",
    "service": "My Service",
    "version": "1.0.0",
    "enclave": true,
    "timestamp": "2025-12-10T00:00:00Z"
}
GET /ready

Readiness probe suitable for Kubernetes.

Notes:

  • Returns 200 when healthy.
  • Returns 503 when degraded/unhealthy.
GET /info

Returns service status with statistics.

Response:

{
    "status": "active",
    "service": "My Service",
    "version": "1.0.0",
    "enclave": true,
    "timestamp": "2025-12-10T00:00:00Z",
    "statistics": {
        "active_jobs": 5,
        "total_requests": 1000
    }
}

net/http ServeMux Integration

Some services are composed into an existing net/http server rather than being served directly from the embedded Gorilla router. In those cases you can register the standard endpoints on a *http.ServeMux:

base.RegisterStandardRoutesOnServeMux(mux)
// or:
base.RegisterStandardRoutesOnServeMuxWithOptions(mux, commonservice.RouteOptions{SkipInfo: true})

Lifecycle Management

Start Sequence
  1. Call Start(ctx) on BaseService
  2. Underlying marble.Service starts
  3. Hydrate function called (if registered)
  4. Background workers launched
Stop Sequence
  1. Call Stop() on BaseService
  2. Stop channel closed (signals workers)
  3. Workers receive stop signal via StopChan()
  4. Underlying marble.Service stops
Safe Stop Handling
// Stop channel is protected by sync.Once - safe to call multiple times
func (b *BaseService) Stop() error {
    b.stopOnce.Do(func() {
        close(b.stopCh)
    })
    return b.Service.Stop()
}

Dependencies

Internal Packages
Package Purpose
infrastructure/marble MarbleRun/EGo integration
infrastructure/database Repository interface
infrastructure/chain Blockchain interaction
infrastructure/httputil HTTP response helpers
External Packages
Package Purpose
github.com/gorilla/mux HTTP router

Services Using This Framework

All Neo Service Layer services extend BaseService:

  • Datafeeds (NeoFeeds): Price feed aggregation
  • Randomness (NeoVRF): Verifiable randomness service (signature + attestation)
  • Automation (NeoFlow): Task automation
  • Confidential Oracle (NeoOracle): External fetch with controls
  • Confidential Compute (NeoCompute): Restricted JS execution
  • AccountPool (NeoAccounts): Account pool management (infrastructure)
  • GlobalSigner: TEE key management + signing (infrastructure)

Documentation

Overview

Package service provides common service infrastructure.

Package service provides common service infrastructure for marble services.

Package service provides common service infrastructure.

Package service provides common service infrastructure for marble services.

Package service provides shared service infrastructure.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DeepHealthHandler

func DeepHealthHandler(checker *DeepHealthChecker, service, version string, enclave bool, uptimeFunc func() time.Duration) http.HandlerFunc

DeepHealthHandler returns an HTTP handler for deep health checks.

func HealthHandler

func HealthHandler(s *BaseService) http.HandlerFunc

HealthHandler returns a standardized /health handler for BaseService.

func InfoHandler

func InfoHandler(s *BaseService) http.HandlerFunc

InfoHandler returns a standardized /info handler for BaseService. It includes statistics from the registered stats function if available.

func MarkDead

func MarkDead()

MarkDead marks the service as not live (triggers restart).

func MarkNotReady

func MarkNotReady()

MarkNotReady marks the service as not ready.

func MarkReady

func MarkReady()

MarkReady marks the service as ready (call after successful startup).

func ReadinessHandler

func ReadinessHandler(s *BaseService) http.HandlerFunc

ReadinessHandler returns a readiness probe handler suitable for k8s.

Types

type BackgroundWorker

type BackgroundWorker interface {
	// Workers returns the number of active background workers.
	Workers() int
}

BackgroundWorker represents a service that runs background tasks.

type BaseConfig

type BaseConfig struct {
	ID      string
	Name    string
	Version string
	Marble  *marble.Marble
	DB      database.RepositoryInterface
	Logger  *logging.Logger
	// RequiredSecrets defines secrets that must be present for the service to be healthy.
	RequiredSecrets []string
}

BaseConfig contains shared configuration for all marbles.

type BaseService

type BaseService struct {
	*marble.Service
	// contains filtered or unexported fields
}

BaseService wraps marble.Service with hydrate/worker wiring and stop handling. It provides a consistent foundation for all marble services with: - Safe stop channel management (sync.Once prevents double-close panic) - Optional hydration hook for loading state on startup - Background worker management - Statistics provider for /info endpoint

func NewBase

func NewBase(cfg *BaseConfig) *BaseService

NewBase constructs a BaseService from shared config.

func (*BaseService) AddTickerWorker

func (b *BaseService) AddTickerWorker(interval time.Duration, fn func(context.Context) error, opts ...TickerWorkerOption) *BaseService

AddTickerWorker registers a periodic background worker. This is a convenience method that wraps the common ticker loop pattern. The worker function is called at the specified interval until Stop() is called.

func (*BaseService) AddWorker

func (b *BaseService) AddWorker(fn func(context.Context)) *BaseService

AddWorker registers a background worker started after hydrate completes. Workers receive the context and should respect context cancellation. Workers should also monitor StopChan() for service shutdown signals.

func (*BaseService) CheckHealth

func (b *BaseService) CheckHealth()

CheckHealth refreshes the cached health state by probing critical dependencies.

func (*BaseService) HealthDetails

func (b *BaseService) HealthDetails() map[string]any

HealthDetails returns a map describing the most recent health state.

func (*BaseService) HealthStatus

func (b *BaseService) HealthStatus() string

HealthStatus returns the aggregated health status string.

func (*BaseService) Logger

func (b *BaseService) Logger() *logging.Logger

Logger returns the service's structured logger.

func (*BaseService) RegisterStandardRoutes

func (b *BaseService) RegisterStandardRoutes()

RegisterStandardRoutes registers the standard /health, /ready, and /info endpoints. This should be called by services that want consistent endpoint behavior.

func (*BaseService) RegisterStandardRoutesOnServeMux

func (b *BaseService) RegisterStandardRoutesOnServeMux(mux *http.ServeMux)

RegisterStandardRoutesOnServeMux registers /health, /ready, and /info on an http.ServeMux. This is useful for services that are composed into an existing net/http server.

func (*BaseService) RegisterStandardRoutesOnServeMuxWithOptions

func (b *BaseService) RegisterStandardRoutesOnServeMuxWithOptions(mux *http.ServeMux, opts RouteOptions)

RegisterStandardRoutesOnServeMuxWithOptions registers standard routes on an http.ServeMux with configurable options. Use SkipInfo: true when the service provides a custom /info.

func (*BaseService) RegisterStandardRoutesWithOptions

func (b *BaseService) RegisterStandardRoutesWithOptions(opts RouteOptions)

RegisterStandardRoutesWithOptions registers standard routes with configurable options. Use SkipInfo: true when the service provides a custom /info endpoint.

func (*BaseService) Start

func (b *BaseService) Start(ctx context.Context) error

Start starts the underlying marble.Service, runs hydrate once, then spins workers.

func (*BaseService) Stop

func (b *BaseService) Stop() error

Stop signals workers and stops the underlying marble.Service. This method is idempotent - calling it multiple times is safe due to sync.Once.

func (*BaseService) StopChan

func (b *BaseService) StopChan() <-chan struct{}

StopChan exposes the stop channel for worker goroutines.

func (*BaseService) WithHydrate

func (b *BaseService) WithHydrate(fn func(context.Context) error) *BaseService

WithHydrate sets an optional hydrate hook executed during Start. The hydrate function is called after the base service starts but before background workers are launched. Use this for loading persistent state.

func (*BaseService) WithStats

func (b *BaseService) WithStats(fn func() map[string]any) *BaseService

WithStats sets a statistics provider function for the /info endpoint. The function will be called on each /info request to get current statistics.

func (*BaseService) WorkerCount

func (b *BaseService) WorkerCount() int

WorkerCount returns the number of registered workers.

func (*BaseService) Workers

func (b *BaseService) Workers() int

Workers returns the number of registered background workers. It is an alias for WorkerCount to satisfy the BackgroundWorker interface.

type BaseSignerAdapter

type BaseSignerAdapter struct {
	GSClient *gsclient.Client
}

BaseSignerAdapter provides common GlobalSigner client operations.

func (*BaseSignerAdapter) GetPublicKey

func (a *BaseSignerAdapter) GetPublicKey(ctx context.Context) (pubKeyHex, keyVersion string, err error)

GetPublicKey gets the current signer public key.

func (*BaseSignerAdapter) IsConfigured

func (a *BaseSignerAdapter) IsConfigured() bool

IsConfigured returns true if the GlobalSigner client is configured.

func (*BaseSignerAdapter) Sign

func (a *BaseSignerAdapter) Sign(ctx context.Context, domain string, data []byte) (signature []byte, keyVersion string, err error)

Sign signs data with a domain prefix using GlobalSigner.

type ChainIntegrated

type ChainIntegrated interface {
	// ChainClient returns the chain client for blockchain interactions.
	ChainClient() *chain.Client
}

ChainIntegrated services interact with the blockchain. This interface helps identify services that need chain connectivity.

type ComponentHealth

type ComponentHealth struct {
	Name      string         `json:"name"`
	Status    string         `json:"status"` // healthy, degraded, unhealthy
	Latency   string         `json:"latency,omitempty"`
	Message   string         `json:"message,omitempty"`
	Details   map[string]any `json:"details,omitempty"`
	CheckedAt time.Time      `json:"checked_at"`
}

ComponentHealth represents the health of a single component.

type DeepHealthChecker

type DeepHealthChecker struct {
	// contains filtered or unexported fields
}

DeepHealthChecker manages multiple component health checks.

func NewDeepHealthChecker

func NewDeepHealthChecker(timeout time.Duration) *DeepHealthChecker

NewDeepHealthChecker creates a new deep health checker.

func (*DeepHealthChecker) Check

func (d *DeepHealthChecker) Check(ctx context.Context, service, version string, enclave bool, uptime time.Duration) *DeepHealthResponse

Check runs all registered health checks and returns aggregated result.

func (*DeepHealthChecker) LastResult

func (d *DeepHealthChecker) LastResult() *DeepHealthResponse

LastResult returns the most recent health check result.

func (*DeepHealthChecker) Register

func (d *DeepHealthChecker) Register(name string, check HealthCheckFunc)

Register adds a health check for a component.

type DeepHealthResponse

type DeepHealthResponse struct {
	Status     string             `json:"status"` // healthy, degraded, unhealthy
	Service    string             `json:"service"`
	Version    string             `json:"version"`
	Enclave    bool               `json:"enclave"`
	Uptime     string             `json:"uptime"`
	Components []*ComponentHealth `json:"components"`
	CheckedAt  time.Time          `json:"checked_at"`
}

DeepHealthResponse is the response for deep health checks.

type HealthCheckFunc

type HealthCheckFunc func(ctx context.Context) *ComponentHealth

HealthCheckFunc is a function that checks a component's health.

func DatabaseHealthCheck

func DatabaseHealthCheck(name string, pingFunc func(context.Context) error) HealthCheckFunc

DatabaseHealthCheck creates a health check for a database connection.

func HTTPHealthCheck

func HTTPHealthCheck(name, url string, timeout time.Duration) HealthCheckFunc

HTTPHealthCheck creates a health check for an HTTP endpoint.

type HealthChecker

type HealthChecker interface {
	// HealthStatus returns the current health status.
	// Returns "healthy", "degraded", or "unhealthy".
	HealthStatus() string

	// HealthDetails returns detailed health information.
	HealthDetails() map[string]any
}

HealthChecker provides custom health check logic. Services implementing this can provide detailed health status.

type HealthResponse

type HealthResponse struct {
	Status    string         `json:"status"`
	Service   string         `json:"service"`
	Version   string         `json:"version"`
	Enclave   bool           `json:"enclave"`
	Timestamp string         `json:"timestamp"`
	Details   map[string]any `json:"details,omitempty"`
}

HealthResponse is the standard response for /health endpoint.

type Hydratable

type Hydratable interface {
	// Hydrate loads persistent state into memory.
	// Called once during service startup.
	Hydrate(ctx context.Context) error
}

Hydratable services can reload state from persistence on startup. This is called during Start() after the base service is initialized but before background workers are started.

type InfoResponse

type InfoResponse struct {
	Status     string         `json:"status"`
	Service    string         `json:"service"`
	Version    string         `json:"version"`
	Enclave    bool           `json:"enclave"`
	Timestamp  string         `json:"timestamp"`
	Statistics map[string]any `json:"statistics,omitempty"`
}

InfoResponse is the standard response for /info endpoint.

type MarbleService

type MarbleService interface {
	// Identity
	ID() string
	Name() string
	Version() string

	// Lifecycle
	Start(ctx context.Context) error
	Stop() error

	// HTTP
	Router() *mux.Router
}

MarbleService is the interface all marble services must implement. This ensures consistent lifecycle management across all services.

type ProbeManager

type ProbeManager struct {
	// contains filtered or unexported fields
}

ProbeManager manages Kubernetes liveness and readiness probes.

func GetProbeManager

func GetProbeManager() *ProbeManager

GetProbeManager returns the default probe manager, creating one if needed.

func NewProbeManager

func NewProbeManager(startupGrace time.Duration) *ProbeManager

NewProbeManager creates a new probe manager.

func (*ProbeManager) InStartupGrace

func (p *ProbeManager) InStartupGrace() bool

InStartupGrace returns whether we're still in the startup grace period.

func (*ProbeManager) IsLive

func (p *ProbeManager) IsLive() bool

IsLive returns whether the service is alive.

func (*ProbeManager) IsReady

func (p *ProbeManager) IsReady() bool

IsReady returns whether the service is ready.

func (*ProbeManager) LivenessHandler

func (p *ProbeManager) LivenessHandler() http.HandlerFunc

LivenessHandler returns an HTTP handler for liveness probes. Returns 200 if live, 503 if not.

func (*ProbeManager) ReadinessHandler

func (p *ProbeManager) ReadinessHandler() http.HandlerFunc

ReadinessHandler returns an HTTP handler for readiness probes. Returns 200 if ready, 503 if not.

func (*ProbeManager) RegisterProbeRoutes

func (p *ProbeManager) RegisterProbeRoutes(mux *http.ServeMux)

RegisterProbeRoutes registers standard Kubernetes probe endpoints.

func (*ProbeManager) SetLive

func (p *ProbeManager) SetLive(live bool)

SetLive marks the service as alive.

func (*ProbeManager) SetReady

func (p *ProbeManager) SetReady(ready bool)

SetReady marks the service as ready to receive traffic.

func (*ProbeManager) StartupHandler

func (p *ProbeManager) StartupHandler() http.HandlerFunc

StartupHandler returns an HTTP handler for startup probes. Returns 200 once startup is complete, 503 during startup.

type ProbeStatus

type ProbeStatus struct {
	Ready   bool   `json:"ready"`
	Live    bool   `json:"live"`
	Message string `json:"message,omitempty"`
}

ProbeStatus represents the status of a probe.

type RouteOptions

type RouteOptions struct {
	SkipInfo bool // Skip /info registration (for services with custom /info)
}

RouteOptions configures which standard routes to register.

type StatisticsProvider

type StatisticsProvider interface {
	// Statistics returns service-specific runtime statistics.
	// The returned map will be included in the /info response under "statistics".
	Statistics() map[string]any
}

StatisticsProvider provides runtime statistics for the /info endpoint. Services implementing this interface will have their statistics included in the standard info response.

type TickerWorkerOption

type TickerWorkerOption func(*tickerWorkerConfig)

TickerWorkerOption configures AddTickerWorker behavior.

func WithTickerWorkerImmediate

func WithTickerWorkerImmediate() TickerWorkerOption

WithTickerWorkerImmediate causes the worker to run once immediately on start (before waiting for the first ticker interval).

func WithTickerWorkerName

func WithTickerWorkerName(name string) TickerWorkerOption

WithTickerWorkerName sets a friendly name used in error logs.

Jump to

Keyboard shortcuts

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