Integrating Fabrica-Util with Roma, Janus, Lares, and Senate: A Complete Guide
Fabrica-Util provides the foundational utility library that unifies runtime behavior across the go-pantheon ecosystem, requiring careful attention to initialization order, package modularity, and version alignment when integrating with Roma, Janus, Lares, and Senate.
Fabrica-Util serves as the core utility library for the go-pantheon microservices architecture, providing shared primitives for time handling, concurrency, cryptography, and distributed ID generation. When integrating this library with Roma (game-logic), Janus (gateway), Lares (account), and Senate (admin) services, developers must follow specific architectural contracts to ensure consistent behavior across the cluster. This guide examines the source code implementation to provide practical integration patterns for each component.
Package Modularity and Import Patterns
Fabrica-Util organizes functionality into discrete, well-typed packages that each service imports selectively. This modular design prevents bloat while maintaining unified runtime behavior across the go-pantheon ecosystem.
Time Handling with xtime
The xtime package provides timezone-aware timestamp operations and period calculations. Roma relies on this for deterministic daily and weekly game resets, while Janus uses it for request timestamp normalization. Lares leverages xtime for time-based token expiration, and Senate schedules maintenance windows using these utilities.
Import path: github.com/go-pantheon/fabrica-util/xtime
Concurrency Primitives with xsync
The xsync package contains delayers, futures, and goroutine lifecycle helpers. Janus manages per-connection timeouts using NewDelayer, Roma spawns background workers with Go helpers, Senate runs periodic jobs, and Lares implements cancellable async flows. The Future type supports context-based cancellation via GetWithContext as implemented in xsync/future.go【L58-L70】.
Import path: github.com/go-pantheon/fabrica-util/xsync
Distributed Identity with xid
The xid package generates zone-aware identifiers using HashID encoding. All services must reference the same player identifier format, with zone bits guaranteeing global uniqueness across data centers. The BuildUID function in xid/id.go【L58-L79】 constructs these identifiers, while EncodeID and DecodeID handle obfuscation.
Import path: github.com/go-pantheon/fabrica-util/xid
Cryptographic Utilities
The security hierarchy provides AES-GCM, RSA, ECDH, and X.509 handling. Lares encrypts authentication tokens, Roma encrypts persisted game state snapshots, Janus secures payloads, and Senate protects configuration files. The security/aes package validates key sizes during NewAESCipher initialization to ensure cross-service compatibility【L20-L27】.
Import path: github.com/go-pantheon/fabrica-util/security/aes
Data Structures for Distributed Systems
Specialized packages support distributed architectures:
- consistenthash: Provides the hash ring implementation used by Roma for player sharding
- bloom: Supplies probabilistic filters for Janus anti-spam detection
- bitmap: Offers compact bit-set storage for Senate flag management
Initialization Order and Configuration
Most Fabrica-Util packages require explicit initialization before use. Skipping these steps causes fallback to safe defaults (such as UTC for time), which may produce inconsistent results across the cluster.
Critical Initialization Sequence
-
xtime.Init: Must execute first in any binary handling timestamps. The function in
xtime/time.go【L22-L53】 parses configuration, loads the location atomically, and falls back to default locale settings. Roma, Janus, and Lares must all call this during startup. -
xid Configuration: While the package initializes automatically via
init()blocks, services must agree on identicalzoneBitandzoneSlotBitlayouts to ensure binary-compatible ID generation. -
Security Setup: Cryptographic packages like
security/aesrequire explicit cipher creation throughNewAESCipher, which validates key sizes for cross-service compatibility.
Error Handling and Context Propagation
All utilities integrate with the project-wide errors wrapper, preserving stack traces and supporting service-wide error policies such as gRPC status conversion. Concurrency primitives expose cancellation through Go's context package.
The Future type provides GetWithContext for deadline propagation, allowing services to cancel asynchronous operations without additional boilerplate【L58-L70】. Similarly, the Delayer supports explicit cancellation methods alongside its timer-based Wait() channel.
Thread Safety and Performance Characteristics
Fabrica-Util implements lock-free and low-contention patterns suitable for high-frequency operations:
- Atomic location storage in
xtimeguarantees lock-free reads after initialization using atomic value storage【L12-L15】 - Delayer uses a single
time.Timerprotected by an RW-mutex, with buffered channels to prevent caller blocking【L45-L50】【L71-L78】 - Future and Then are fully generic, avoiding unnecessary allocations suitable for Roma's game loops
Version Compatibility and Dependency Management
Fabrica-Util targets Go 1.24+ across all packages. Services must align go.mod dependencies to the same vX.Y.Z tag; mismatched versions can break binary-compatible hashid layouts used by xid.
The repository enforces a strict license policy permitting only MIT, Apache-2.0, BSD, ISC, and MPL-2.0 dependencies, preventing legal complications when shipping services【L13-L22】.
Practical Integration Examples
Initializing Time Utilities in Roma and Janus
import (
"log"
"github.com/go-pantheon/fabrica-util/xtime"
)
func initTime() {
if err := xtime.Init(xtime.Config{
Language: "en",
Timezone: "Asia/Shanghai",
}); err != nil {
log.Fatalf("time init failed: %v", err)
}
}
This initialization pattern appears in xtime/time.go【L22-L53】, loading the timezone location atomically for subsequent operations.
Generating Zone-Based IDs for Lares and Roma
import (
"fmt"
"github.com/go-pantheon/fabrica-util/xid"
)
func playerIDExample() {
uid, _ := xid.BuildUID(12345, 3) // zone 3
enc, _ := xid.EncodeID(uid) // hashid string
fmt.Println("Encoded:", enc)
dec, _ := xid.DecodeID(enc)
gameID, zone := xid.SplitUID(dec)
fmt.Printf("Decoded gameID=%d zone=%d\n", gameID, zone)
}
The xid/id.go implementation【L58-L79】 provides zone-aware construction and HashID encoding.
Managing Connection Timeouts in Janus
import (
"fmt"
"time"
"github.com/go-pantheon/fabrica-util/xsync"
)
func connTimeoutDemo() {
d := xsync.NewDelayer()
expireAt := time.Now().Add(5 * time.Second)
d.SetExpiryTime(expireAt)
fmt.Println("Waiting for expiry…")
<-d.Wait() // blocks until timer fires
fmt.Println("Connection timed out")
}
The Delayer implementation in xsync/delayer.go uses buffered channels and RW-mutex protection【L45-L50】【L71-L78】.
Chaining Asynchronous Operations in Roma
import (
"context"
"fmt"
"time"
"github.com/go-pantheon/fabrica-util/xsync"
)
func asyncChain() {
f := xsync.NewFuture[int]()
go func() {
// simulate work
f.Complete(42, nil)
}()
// Transform the result
f2 := xsync.Then[int, string](f, func(v int) (string, error) {
return fmt.Sprintf("value=%d", v), nil
})
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
s, err := f2.GetWithContext(ctx)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("final:", s) // -> "final: value=42"
}
The Then function and context-aware retrieval appear in xsync/future.go【L58-L70】【L93-L112】.
Encrypting Sensitive Data Across Services
import (
"fmt"
"github.com/go-pantheon/fabrica-util/security/aes"
)
func encryptDemo() {
key := []byte("0123456789abcdef0123456789abcdef") // 32-byte key
c, _ := aes.NewAESCipher(key)
plain := []byte("super-secret")
ciphertext, _ := c.Encrypt(plain)
fmt.Printf("cipher: %x\n", ciphertext)
dec, _ := c.Decrypt(ciphertext)
fmt.Println("decrypted:", string(dec))
}
The AES-GCM implementation in security/aes/aes.go validates key sizes during initialization【L20-L38】.
Key Source Files and Architecture
| File | Package | Role |
|---|---|---|
xtime/time.go |
xtime |
Timezone handling and atomic location storage |
xsync/delayer.go |
xsync |
Timer-based expiry with RW-mutex protection |
xsync/future.go |
xsync |
Generic async results with context cancellation |
xid/id.go |
xid |
Zone-aware ID construction and HashID encoding |
security/aes/aes.go |
security/aes |
AES-GCM encryption with PKCS7 padding |
consistenthash/* |
consistenthash |
Distributed hash ring for Roma sharding |
bloom/* |
bloom |
Probabilistic filters for Janus spam detection |
bitmap/* |
bitmap |
Compact bit-sets for Senate flag storage |
Summary
- Initialize
xtimefirst: Callxtime.Initbefore any timestamp operations to ensure timezone consistency across Roma, Janus, and Lares - Align zone configurations: Ensure identical
zoneBitlayouts inxidacross all services to maintain ID compatibility - Use context propagation: Leverage
GetWithContextinxsyncprimitives to respect request deadlines without boilerplate - Verify Go versions: Target Go 1.24+ and align Fabrica-Util versions across all services to prevent HashID incompatibilities
- Respect thread-safety: Rely on atomic operations in
xtimeand buffered channels inDelayerfor high-concurrency scenarios
Frequently Asked Questions
What happens if xtime.Init is not called before using time functions?
Functions will fall back to safe defaults such as UTC, but this produces inconsistent results across the cluster. According to the implementation in xtime/time.go, the Init function loads the timezone location atomically; without it, services may calculate different daily reset times or token expirations.
How do I ensure ID compatibility between different go-pantheon services?
All services must use the same zoneBit and zoneSlotBit configuration when calling xid functions. The BuildUID and DecodeID functions in xid/id.go rely on these bit layouts; mismatched configurations between Roma and Lares will cause player ID collisions or decoding failures.
Is Fabrica-Util thread-safe for high-concurrency scenarios?
Yes. The library uses atomic value storage for timezone locations in xtime, RW-mutexes for the Delayer timer state, and lock-free patterns in Future implementations. These designs support high-frequency operations in Roma's game loops and Janus's connection handling without contention.
What Go version is required to use Fabrica-Util?
Fabrica-Util targets Go 1.24+ across all packages. The repository enforces this requirement to utilize modern language features and maintain compatibility. Services must update their go.mod files accordingly, as older versions may not support the generic implementations in xsync or the atomic patterns in xtime.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →