Server API Reference
The Saturn CLI server package provides the infrastructure for creating job execution servers. This document details the server API and how to use it in your Go applications for managing background processes.
Package Overview
The server package enables you to create custom Saturn job servers that can execute tasks sent by clients. It handles job registration, execution, and lifecycle management.
Import the server package:
import "github.com/Kingson4Wu/saturncli/server"
Creating a Server
NewServer
Creates a new server instance with the specified logger and socket path:
func NewServer(logger utils.Logger, sockPath string, opts ...Option) *ser
Parameters:
logger: A logger implementation that satisfies theutils.LoggerinterfacesockPath: Path to the Unix domain socket or TCP address for the serveropts: Optional configuration options (see Options below)
Example:
server := server.NewServer(&utils.DefaultLogger{}, "/tmp/saturn.sock")
server.Serve()
Options
WithRegistry
Allows you to specify a custom job registry instead of using the default global registry:
func WithRegistry(registry *Registry) Option
Example:
registry := server.NewRegistry()
server := server.NewServer(
&utils.DefaultLogger{},
"/tmp/saturn.sock",
server.WithRegistry(registry),
)
Server Methods
Serve
Starts the server and begins listening for client connections. This method blocks the calling goroutine:
func (s *ser) Serve()
Job Registration
Saturn supports two types of jobs: regular jobs and stoppable jobs.
Global Registration Functions
The server package provides global registration functions that operate on the default global registry:
AddJob
Registers a regular (non-stoppable) job globally:
func AddJob(name string, handler JobHandler) error
AddStoppableJob
Registers a stoppable job globally:
func AddStoppableJob(name string, handler StoppableJobHandler) error
Registry-Based Registration
For more control, create a specific registry:
NewRegistry
Creates a new isolated job registry:
func NewRegistry() *Registry
Registry.AddJob
Registers a regular job in the specified registry:
func (r *Registry) AddJob(name string, handler JobHandler) error
Parameters:
name: Unique identifier for the jobhandler: Function that implements the job logic
Registry.AddStoppableJob
Registers a stoppable job in the specified registry:
func (r *Registry) AddStoppableJob(name string, handler StoppableJobHandler) error
Parameters:
name: Unique identifier for the jobhandler: Function that implements the stoppable job logic
Job Handler Types
JobHandler
Function type for regular jobs:
type JobHandler func(args map[string]string, signature string) bool
Parameters:
args: Map of parameters passed from the clientsignature: Unique identifier for this job run
Return Value:
bool:truefor success,falsefor failure
StoppableJobHandler
Function type for stoppable jobs:
type StoppableJobHandler func(args map[string]string, signature string, quit chan struct{}) bool
Parameters:
args: Map of parameters passed from the clientsignature: Unique identifier for this job runquit: Channel that signals when the job should stop
Return Value:
bool:truefor success,falsefor failure
Job Handler Patterns
Regular Job Pattern
Regular jobs execute and return without the ability to be stopped externally:
func myJobHandler(args map[string]string, signature string) bool {
// Process arguments
id := args["id"]
version := args["version"]
// Perform job work
log.Printf("Executing job %s with id=%s, version=%s", signature, id, version)
// Do the work...
// Return true for success, false for failure
return true
}
Stoppable Job Pattern
Stoppable jobs must regularly check the quit channel to allow graceful shutdown:
func myStoppableJobHandler(args map[string]string, signature string, quit chan struct{}) bool {
// Process arguments
id := args["id"]
// Use a ticker to periodically check for quit signal
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for i := 0; ; i++ {
select {
case <-quit:
log.Printf("Job %s received quit signal at iteration %d", signature, i)
return true // Indicate successful clean shutdown
case <-ticker.C:
// Do work for this iteration
log.Printf("Processing iteration %d for job %s", i, signature)
// Simulate work
time.Sleep(time.Second)
}
}
}
Complete Usage Example
Here's a complete example showing how to create a custom Saturn server:
package main
import (
"fmt"
"log"
"time"
"github.com/Kingson4Wu/saturncli/server"
"github.com/Kingson4Wu/saturncli/utils"
)
func main() {
// Create a registry to organize jobs
registry := server.NewRegistry()
// Register a regular job
if err := registry.AddJob("hello", func(args map[string]string, signature string) bool {
log.Printf("Hello job running with args: %+v, signature: %s", args, signature)
// Simulate some processing time
time.Sleep(1 * time.Second)
// Optionally return false if job fails
if args["fail"] == "true" {
log.Printf("Failing job %s intentionally", signature)
return false
}
return true
}); err != nil {
log.Fatal("Failed to add job:", err)
}
// Register a stoppable job
if err := registry.AddStoppableJob("countdown", func(args map[string]string, signature string, quit chan struct{}) bool {
count := 10
if args["count"] != "" {
fmt.Sscanf(args["count"], "%d", &count)
}
log.Printf("Countdown job %s starting for %d seconds", signature, count)
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for i := 0; i < count; i++ {
select {
case <-quit:
log.Printf("Countdown job %s stopped early at step %d", signature, i)
return true
case <-ticker.C:
log.Printf("Countdown job %s: %d remaining", signature, count-i)
}
}
log.Printf("Countdown job %s completed normally", signature)
return true
}); err != nil {
log.Fatal("Failed to add stoppable job:", err)
}
// Create and start the server with our custom registry
server.NewServer(
&utils.DefaultLogger{},
"/tmp/my_custom_saturn.sock",
server.WithRegistry(registry),
).Serve()
}
Server Lifecycle
- Create a registry (optional, defaults to global registry)
- Register your jobs with handlers
- Create a server instance
- Call
Serve()to start the server - The server listens for client connections until terminated
- Unix socket files are cleaned up automatically on startup
Platform-Specific Behavior
- Unix-like systems (macOS/Linux): Uses Unix domain sockets
- Windows: Falls back to TCP connections on localhost
Best Practices
-
Unique Job Names: Ensure job names are unique within each registry to prevent conflicts.
-
Proper Cleanup: Stoppable jobs must properly handle the quit channel to allow graceful shutdown.
-
Error Handling: Return
falsefrom job handlers when errors occur,truefor success. -
Logging: Use the provided logger for consistent logging across your application.
-
Thread Safety: Job handlers may be called concurrently, so ensure thread-safe operations.
-
Resource Management: Clean up resources properly in job handlers to prevent leaks.
-
Validation: Validate input parameters in job handlers to prevent unexpected behavior.
See Also
- Client API Reference - For using the Saturn client in your applications
- Embedding Guide - For integrating Saturn into your services
- Architecture - For understanding the system design