# How fabrica-util Handles Distributed ID Generation Across Multiple Game Server Zones

> Discover how fabrica-util generates unique distributed IDs across game server zones by embedding zone identifiers into 64-bit integers. Achieve global uniqueness without central coordination.

- Repository: [Pantheon/fabrica-util](https://github.com/go-pantheon/fabrica-util)
- Tags: how-to-guide
- Published: 2026-03-02

---

**fabrica-util** solves distributed ID generation across multiple game server zones by embedding zone identifiers directly into 64-bit integers, eliminating the need for central coordination while guaranteeing global uniqueness.

Distributed game architectures require unique identifiers that span multiple server zones without collisions. The **go-pantheon/fabrica-util** repository provides a compact, zone-aware solution through its **`xid`** package, which embeds region data directly into the ID bit structure. This approach enables stateless generation across distributed game server zones, ensuring that a player created in zone 12 receives an ID that never conflicts with one generated in zone 34, even when both use the same local game identifier.

## 64-Bit Bit Layout and Zone Architecture

The core mechanism resides in [`xid/id.go`](https://github.com/go-pantheon/fabrica-util/blob/main/xid/id.go), where the 64-bit integer is partitioned into distinct fields to encode both the game-specific entity and its origin zone.

The layout allocates space as follows:

- **Game ID**: Occupies the high bits (63 down to `gameIDSlotBit`), providing the entity identifier space
- **Zone**: Occupies the low 8 bits (`zoneSlotBit`), supporting 256 distinct zones
- **Sign bit**: Remains untouched to ensure positive values

Key constants defined in the source include:

- `gameIDSlotBit = 16`: Defines the boundary between game ID and zone data
- `zoneBit = 8`: Size of the zone field
- `MaxZone = 255`: The highest valid zone value

This structure ensures that two servers operating in different zones produce different 64-bit outputs even when incrementing the same local `gameID` counter.

## Core API for Zone-Aware ID Management

### Building UIDs with BuildUID

The **`BuildUID`** function (lines 58-69 in [`xid/id.go`](https://github.com/go-pantheon/fabrica-util/blob/main/xid/id.go)) constructs the composite identifier. It validates the `gameID` against `MaxGameID` and `MinGameID` thresholds, then left-shifts the game ID by `gameIDSlotBit` positions before bitwise-ORing the zone value.

### Extracting Components with SplitUID

To retrieve the original values, **`SplitUID`** (lines 71-78) reverses the operation. It right-shifts the UID to isolate the game ID and masks the lower bits to extract the zone identifier.

### External Encoding with Hashids

For client-facing representation, **`EncodeID`** (lines 81-94) transforms the integer into an 18-character base-62 string using the Hashids algorithm. Negative IDs bypass this encoding and render as plain decimal strings. The complementary **`DecodeID`** function (lines 96-112) reverses this process, ensuring the system can handle signed integer edge cases safely.

## Advantages for Distributed Game Servers

This architecture delivers specific benefits for multi-zone deployments:

- **Stateless Operation**: The `BuildUID` function requires only local knowledge of the server's zone constant and the next available game ID. No network calls or shared databases are necessary during generation.
- **Collision Prevention**: Because the zone occupies dedicated bits, zone 12 and zone 13 produce mathematically distinct ID spaces, eliminating collision risks across regions.
- **Database Efficiency**: The zone information travels with the ID itself, removing the need for separate sharding columns or lookup tables when routing requests.
- **Predictable Limits**: With `MaxZone` fixed at 255 and remaining bits allocated to game IDs, capacity planning is straightforward for operators managing shard growth.

## Practical Implementation Example

The following example demonstrates the complete lifecycle from generation to decoding:

```go
package main

import (
	"fmt"
	"log"

	"github.com/go-pantheon/fabrica-util/xid"
)

func main() {
	// Suppose this server belongs to zone 12 (e.g., "EU‑West")
	const zone uint8 = 12

	// A game‑specific identifier, e.g., a sequential player number
	var gameID int64 = 123456

	// Create a zone‑aware UID
	uid, err := xid.BuildUID(gameID, zone)
	if err != nil {
		log.Fatalf("cannot build UID: %v", err)
	}
	fmt.Printf("Combined UID (int64): %d\n", uid)

	// Encode the UID for external use (e.g., in URLs)
	encoded, err := xid.EncodeID(uid)
	if err != nil {
		log.Fatalf("cannot encode UID: %v", err)
	}
	fmt.Printf("Encoded UID (string): %s\n", encoded)

	// Later, decode back to the original integer
	decoded, err := xid.DecodeID(encoded)
	if err != nil {
		log.Fatalf("cannot decode UID: %v", err)
	}
	fmt.Printf("Decoded UID (int64): %d\n", decoded)

	// Split the UID to retrieve gameID and zone
	splitGameID, splitZone := xid.SplitUID(decoded)
	fmt.Printf("Extracted gameID: %d, zone: %d\n", splitGameID, splitZone)
}

```

## Summary

- **fabrica-util** embeds zone identifiers into 64-bit integers using the `xid` package to enable distributed ID generation across multiple game server zones.
- The bit layout reserves 8 bits for zone data (supporting 256 zones) and the upper bits for game-specific identifiers, implemented in [`xid/id.go`](https://github.com/go-pantheon/fabrica-util/blob/main/xid/id.go).
- **`BuildUID`** combines local game IDs with zone constants via bit-shifting, while **`SplitUID`** extracts these components for reverse lookup.
- **Hashids encoding** provides URL-safe, 18-character string representations for client communication without exposing raw bit patterns.
- This stateless approach requires no central coordinator, allowing independent ID generation across all zones while maintaining global uniqueness.

## Frequently Asked Questions

### How does fabrica-util ensure unique IDs across different zones without coordination?

By dedicating the lower 8 bits of every 64-bit ID to the zone identifier, fabrica-util mathematically partitions the ID space. A game ID of 123456 in zone 12 produces a different 64-bit integer than the same game ID in zone 13, preventing collisions regardless of generation timing or server synchronization.

### What is the maximum number of game server zones supported by this system?

The system supports **256 zones** (0-255), defined by the `MaxZone` constant and the 8-bit zone field in [`xid/id.go`](https://github.com/go-pantheon/fabrica-util/blob/main/xid/id.go). This accommodates most regional sharding strategies while reserving sufficient bits for high-volume game entity identifiers.

### Can fabrica-util handle negative ID values?

Yes. The `EncodeID` function detects negative integers and returns them as plain decimal strings rather than Hashids-encoded values. This ensures that error codes or sentinel values using negative numbers pass through the encoding layer without corruption, as implemented in lines 81-94 of [`xid/id.go`](https://github.com/go-pantheon/fabrica-util/blob/main/xid/id.go).

### Why choose Hashids over standard Base64 for external ID representation?

Hashids generates 18-character, base-62 strings that are URL-safe and visually distinct, avoiding ambiguous characters that could confuse players. The implementation in [`xid/id.go`](https://github.com/go-pantheon/fabrica-util/blob/main/xid/id.go) uses the `github.com/speps/go-hashids/v2` library to create compact strings that obscure the underlying database sequence while remaining reversible via `DecodeID`.