• Overview
  • Deployment
  • Configuration
    • Config
    • Auth Config
    • Codecs Sets
  • REST API
  • Metrics
  • Schema
  • Commands
  • SDKs
  • RzGate
  • Bulk Load
  • Benchmark
  • Quic Start
    • Single Node
    • Clustered
  • Service Discovery
  • FAQ
Roomzin RzGate SDKs Benchmarks Download Pricing Docs Architecture Contact Us

What is Roomzin?

Roomzin is a fast, lightweight inventory engine built for booking platforms. It's a single binary (<5 MB) with no external dependencies that can be deployed standalone or as a self-discovering cluster.

The Problem Roomzin Solves

Traditional caching layers struggle with complex travel queries. Booking platforms need to search across multiple filters — price, amenities, location, availability. This typically requires complex cache key design, manual invalidation, dozens of round-trips to join scattered data, and falls back to slow database queries on cache misses.

How Roomzin Helps

Roomzin answers complex inventory queries in a single round-trip, returning every search hit for the hot window. It's designed to handle the ~95% of live traffic that falls within the booking hot-window, while complementing Redis for static key/value operations.

Single Node Quick Start

Get Roomzin Running in Standalone Mode

This guide walks you through setting up a single-node Roomzin instance and performing a simple search using SDKs. We'll use standalone mode for simplicity—no clustering involved.

1

Configure

Set up configuration files

2

Start Server

Launch Roomzin in standalone mode

3

Connect SDK

Query with your preferred language

Configuration Setup

Create three YAML configuration files in your working directory:

roomzin.yml

tcp:
  listen_addr: "127.0.0.1"
  port: 7777
  max_connections: 1000

auth_file: "auth.yml"
api_port: 8080
data_dir: "./data"
maintenance_hour: 3

auth.yml

tokens:
  - token: abc123
    role: admin
  - token: client-token
    role: client

codecs.yml

24 rate features max

Important: Defines allowed rate features like cancellation policies and room amenities. Order is critical for internal mapping. Changes require admin operation and full restart.

rate_features:
  - free_featurelation
  - non_refundable
  - pay_at_property
  - includes_breakfast
  - free_wifi
  - no_prepayment
  - partial_refund
  - instant_confirmation

Start Roomzin Server

Launch Roomzin in standalone mode:

Terminal
Standalone mode
./roomzin run --config ./roomzin.yml --codecs ./codecs.yml

Connect & Query with SDK

Go Example
SDK v1.0+
package main

import (
    "fmt"
    "time"
    "github.com/roomzin/roomzin-go/single"
    "github.com/roomzin/roomzin-go/types"
)

func main() {
    cfg := single.Config{
        Host:      "127.0.0.1",
        TCPPort:   7777,
        AuthToken: "client-token",
    }
    client, err := single.New(&cfg)
    if err != nil {
        panic(err)
    }
    defer client.Close()

    err = client.SetProp(types.SetPropPayload{
        Segment:      "segment_1",
        Area:         "New York",
        PropertyID:   "550e8400-e29b-41d4-a716-446655440000",
        PropertyType: "hotel",
        Category:     "luxury",
        Stars:        4,
        Latitude:     40.7138,
        Longitude:    -74.0050,
        Amenities:    []string{"wifi", "pool", "gym"},
    })
    if err != nil {
        panic(err)
    }

    dates := make([]string, 0, 10)
    tomorrow := time.Now().AddDate(0, 0, 1)
    for i := 0; i < 10; i++ {
        dates = append(dates, tomorrow.AddDate(0, 0, i).Format("2006-01-02"))
    }
    for i := 0; i < 10; i++ {
        avail := uint8(10)
        price := uint32(150)
        err = client.SetRoomPkg(types.SetRoomPkgPayload{
            PropertyID:   "prop_1",
            RoomType:     "single",
            Date:         dates[i],
            Availability: &avail,
            FinalPrice:   &price,
            RateFeature:   []string{"free_featurelation", "pay_at_property"},
        })
        if err != nil {
            panic(err)
        }
    }
    var limit uint64 = 10
    results, err := client.SearchAvail(types.SearchAvailPayload{
        Segment:    "segment_1",
        RoomType:   "single",
        Date:       dates[:2],
        Limit:      &limit,
    })
    if err != nil {
        panic(err)
    }
    for _, prop := range results {
        fmt.Printf("Property: %s\n", prop.PropertyID)
        for _, day := range prop.Days {
            fmt.Printf("  Date: %s, Avail: %d, Price: %d\n", day.Date, day.Availability, day.FinalPrice)
        }
    }
}
Java Example
SDK v1.0+
import java.time.Duration;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import com.roomzin.roomzinjava.api.CacheClientApi;
import com.roomzin.roomzinjava.client.single.SingleClient;
import com.roomzin.roomzinjava.client.single.SingleConfig;
import com.roomzin.roomzinjava.types.SetPropPayload;
import com.roomzin.roomzinjava.types.SetRoomPkgPayload;
import com.roomzin.roomzinjava.types.SearchAvailPayload;
import com.roomzin.roomzinjava.types.PropertyAvail;

public class QuickStart {
    public static void main(String[] args) {
        SingleConfig config = SingleConfig.builder()
            .withHost("127.0.0.1")
            .withTcpPort(7777)
            .withAuthToken("client-token")
            .withTimeout(Duration.ofSeconds(5))
            .build();

        try (CacheClientApi client = new SingleClient(config)) {
            // Set property
            client.setProp(SetPropPayload.builder()
                .segment("segment_1")
                .area("New York")
                .propertyId("550e8400-e29b-41d4-a716-446655440000")
                .propertyType("hotel")
                .category("luxury")
                .stars((short) 4)
                .latitude(40.7138)
                .longitude(-74.0050)
                .amenities(List.of("wifi", "pool", "gym"))
                .build());

            // Set room packages for 10 days
            List dates = new ArrayList<>();
            LocalDate tomorrow = LocalDate.now().plusDays(1);
            for (int i = 0; i < 10; i++) {
                dates.add(tomorrow.plusDays(i).toString());
            }
            for (String date : dates) {
                client.setRoomPkg(SetRoomPkgPayload.builder()
                    .propertyId("prop_1")
                    .roomType("single")
                    .date(date)
                    .availability((short) 10)
                    .finalPrice(150)
                    .RateFeature(List.of("free_featurelation", "pay_at_property"))
                    .build());
            }

            // Search availability
            List results = client.searchAvail(SearchAvailPayload.builder()
                .segment("segment_1")
                .roomType("single")
                .date(dates.subList(0, 2))
                .limit(10L)
                .build());

            for (PropertyAvail prop : results) {
                System.out.println("Property: " + prop.getPropertyId());
                for (DayAvail day : prop.getDays()) {
                    System.out.printf("  Date: %s, Avail: %d, Price: %d%n", 
                        day.getDate(), day.getAvailability(), day.getFinalPrice());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Python Example
SDK v1.0+
from roomzin_py.single import new_config_builder, new_client
from roomzin_py.api import CacheClientAPI
from roomzin_py.types.request import SetPropPayload, SetRoomPkgPayload, SearchAvailPayload
from roomzin_py.types.response import PropertyAvail
from datetime import datetime, timedelta

config = new_config_builder() \
    .with_host("127.0.0.1") \
    .with_tcp_port(7777) \
    .with_token("client-token") \
    .with_timeout(5.0) \
    .build()

client: CacheClientAPI = new_client(config)
try:
    # Set property
    client.set_prop(SetPropPayload(
        segment="segment_1",
        area="New York",
        property_id="550e8400-e29b-41d4-a716-446655440000",
        property_type="hotel",
        category="luxury",
        stars=4,
        latitude=40.7138,
        longitude=-74.0050,
        amenities=["wifi", "pool", "gym"]
    ))

    # Set room packages for 10 days
    dates = []
    tomorrow = datetime.now() + timedelta(days=1)
    for i in range(10):
        dates.append((tomorrow + timedelta(days=i)).strftime("%Y-%m-%d"))
    for date in dates:
        client.set_room_pkg(SetRoomPkgPayload(
            property_id="prop_1",
            room_type="single",
            date=date,
            availability=10,
            final_price=150,
            rate_feature=["free_featurelation", "pay_at_property"]
        ))

    # Search availability
    results = client.search_avail(SearchAvailPayload(
        segment="segment_1",
        room_type="single",
        date=dates[:2],
        limit=10
    ))
    for prop in results:
        print(f"Property: {prop.property_id}")
        for day in prop.days:
            print(f"  Date: {day.date}, Avail: {day.availability}, Price: {day.final_price}")
finally:
    client.close()
C# Example
SDK v1.0+
using System;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.Generic;
using Roomzin.Sdk.Internal.Api;
using Roomzin.Sdk.Internal.Single;
using Roomzin.Sdk.Types.Requests;
using Roomzin.Sdk.Types.Responses;

class QuickStart
{
    static async Task Main()
    {
        var config = new ConfigBuilder()
            .WithHost("127.0.0.1")
            .WithTcpPort(7777)
            .WithToken("client-token")
            .WithTimeout(TimeSpan.FromSeconds(5))
            .Build();

        await using var client = Client.New(config);
        try
        {
            // Set property
            await client.SetPropAsync(new SetPropPayload
            {
                Segment = "segment_1",
                Area = "New York",
                PropertyId = "550e8400-e29b-41d4-a716-446655440000",
                PropertyType = "hotel",
                Category = "luxury",
                Stars = 4,
                Latitude = 40.7138,
                Longitude = -74.0050,
                Amenities = new[] { "wifi", "pool", "gym" }
            });

            // Set room packages for 10 days
            var dates = new List();
            var tomorrow = DateTime.Now.AddDays(1);
            for (int i = 0; i < 10; i++)
            {
                dates.Add(tomorrow.AddDays(i).ToString("yyyy-MM-dd"));
            }
            foreach (var date in dates)
            {
                await client.SetRoomPkgAsync(new SetRoomPkgPayload
                {
                    PropertyId = "prop_1",
                    RoomType = "single",
                    Date = date,
                    Availability = 10,
                    FinalPrice = 150,
                    RateFeature = new[] { "free_featurelation", "pay_at_property" }
                });
            }

            // Search availability
            var results = await client.SearchAvailAsync(new SearchAvailPayload
            {
                Segment = "segment_1",
                RoomType = "single",
                Date = dates.Take(2).ToArray(),
                Limit = 10
            });

            foreach (var prop in results)
            {
                Console.WriteLine($"Property: {prop.PropertyId}");
                foreach (var day in prop.Days)
                {
                    Console.WriteLine($"  Date: {day.Date}, Avail: {day.Availability}, Price: {day.FinalPrice}");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}
Node.js Example
SDK v1.0+
import { SingleClient } from 'roomzin-js';
import { CacheClientAPI } from 'roomzin-js';

async function main() {
    const client: CacheClientAPI = await SingleClient.create({
        host: '127.0.0.1',
        tcpPort: 7777,
        authToken: 'client-token',
        timeout: 5000,
        keepAlive: 30000,
    });

    try {
        // Set property
        await client.setProp({
            segment: 'segment_1',
            area: 'New York',
            propertyID: '550e8400-e29b-41d4-a716-446655440000',
            propertyType: 'hotel',
            category: 'luxury',
            stars: 4,
            latitude: 40.7138,
            longitude: -74.0050,
            amenities: ['wifi', 'pool', 'gym'],
        });

        // Set room packages for 10 days
        const dates: string[] = [];
        const tomorrow = new Date();
        tomorrow.setDate(tomorrow.getDate() + 1);
        for (let i = 0; i < 10; i++) {
            const d = new Date(tomorrow);
            d.setDate(d.getDate() + i);
            dates.push(d.toISOString().split('T')[0]);
        }
        for (const date of dates) {
            await client.setRoomPkg({
                propertyID: 'prop_1',
                roomType: 'single',
                date,
                availability: 10,
                finalPrice: 150,
                RateFeature: ['free_featurelation', 'pay_at_property'],
            });
        }

        // Search availability
        const results = await client.searchAvail({
            segment: 'segment_1',
            roomType: 'single',
            date: dates.slice(0, 2),
            limit: 10,
        });

        for (const prop of results) {
            console.log(`Property: ${prop.propertyID}`);
            for (const day of prop.days) {
                console.log(`  Date: ${day.date}, Avail: ${day.availability}, Price: ${day.finalPrice}`);
            }
        }
    } catch (err) {
        console.error(err);
    } finally {
        await client.close?.();
    }
}

main();

Cluster Quick Start

Local Docker Testing Environment

Testing Environment Only

This Docker setup is for local testing only. Production deployment uses bare metal/VMs for optimal performance.

Production Reality: Single binary + three files on bare metal/VMs. This Docker playground lets you test cluster features like auto-joining and snapshot loading in minutes.


The Roomzin Quickstart repository provides a complete Docker-based testing environment for a 3-node Roomzin cluster with optional monitoring (Prometheus + Grafana).

What You Get

  • 3-node Roomzin cluster – pre-configured with static discovery and TLS certificates
  • Optional monitoring – Prometheus + Grafana with auto-imported Roomzin dashboard
  • Standalone HTML dashboard – quick visual overview without Grafana
  • Makefile automation – one-command start/stop/test

Quick Start Commands

git clone https://github.com/m-javani/roomzin-quickstart
cd roomzin-quickstart
docker pull mehdyjavany/roomzin:latest
docker pull mehdyjavany/rzgate:latest
make stop && make start
Get Roomzin Quickstart →

Complete documentation, examples, and setup instructions available on GitHub

Note: The cluster can start fresh or load a user-provided snapshot created as described in the Bulk Load section. See the repository README for detailed instructions.

Data Model at a Glance

Everything hinges on three primitives:

1

Property

Hotel or accommodation unit

2

Room Type

Sellable room category

3

Daily Package

Bookable daily inventory

Together they form a self-contained, in-memory state machine that powers every update and search without external caches or joins.

Roomzin loads the "hot window" (15, 30, 60 days or more) and keeps it mutable—add, remove, or patch availability and prices in real time while queries remain instant.

Property

A hotel or accommodation unit, sharded into segments that mirror real-world search patterns. Paris's ~1.8K hotels, for instance, split into 5 segments for parallel ingest and query.

Key Attributes
segment area property-id type category stars geo amenities

Room Type

The sellable rooms—Standard Double, Deluxe Suite, etc.—that belong to each property.

Daily Package

A daily, bookable slice of a room type within the hot window, carrying:

Package Components
date availability final price rate features

Zero-Ops, In-Memory Inventory Engine

Drop Roomzin in and you get a zero-ops, in-memory inventory engine—no caches, expiry headaches, or joins required. Plug in and run at speed.

Constraints by Design

Deliberate performance trade-offs for optimal speed and reliability

Critical Considerations

  • Any change to codecs.yml will invalidate existing queries
  • Requires a full restart of the cluster or standalone instance
  • All existing snapshots will be dropped as they become invalid
  • Modifications should be treated as an admin-level operation

Codecs Configuration

Rate features must be defined in codecs.yml file. This file follows a specific schema, with up to 24 rate features.

Performance Trade-off: The limits of 24 rate features may seem restrictive, but they are a deliberate trade-off for optimal performance.

Schema Definition

The schema for codecs.yml:

codecs.yml
rate_features:
  - free_featurelation
  - non_refundable
  # ... up to 24 entries

Order Sensitivity: The order of entries is critical, as Roomzin uses this file for internal mapping.

Rate / Cancellation

Max: 24 entries

Example values (comma-separated):

free cancellation non-refundable pay at property free Wi-Fi includes breakfast no prepayment partial refund instant confirmation

Deployment

Infrastructure-agnostic, performance-first

Roomzin is a standalone binary optimized for microsecond-level latency. It runs natively on any infrastructure — from dedicated servers to virtual machines — without external dependencies. For maximum throughput and deterministic performance, deploy Roomzin directly on the host system and avoid additional runtime layers such as containers or orchestration stacks.


Important: Roomzin detects CPU core changes at runtime. If the hardware (number of cores) is modified while running, the application will automatically shut down for safety. Vertical scaling requires a restart — do not hot-add/remove CPUs without restarting the process.

Stand-alone Deployment

Single-node or development environments

Just drop the binary next to its config, auth and codecs.yml file and run.

roomzin run --config /path/to/roomzin.yml --codecs /path/to/codecs.yml

Clustered Deployment

High-availability production cluster

Place the required files (config, auth, codecs, *.pem) on each machine and start the same binary as shown below. Nodes auto-discover each other—only a single seed is enough to pull the full peer list, provided the cluster graph stays connected.

Cluster Formation Modes

Founding Voter

Include --initial_voters with identical node lists on all founding nodes

Learner

Omit --initial_voters to join as non-voting replica

Three-node Founding Cluster Example

Example for shard1, zone1:

Machine 1
./roomzin run-clustered --config roomzin.yml --codecs ./config/codecs.yml --node-id node1 --shard-id shard1 --zone-id zone1 --initial-voters "node1,node2,node3"
Machine 2
./roomzin run-clustered --config roomzin.yml --codecs ./config/codecs.yml --node-id node2 --shard-id shard1 --zone-id zone1 --initial-voters "node1,node2,node3"
Machine 3
./roomzin run-clustered --config roomzin.yml --codecs ./config/codecs.yml --node-id node3 --shard-id shard1 --zone-id zone1 --initial-voters "node1,node2,node3"

Adding a Learner Node to Existing Cluster

Machine 4 (Learner)
./roomzin run-clustered --config roomzin.yml --codecs ./config/codecs.yml --node-id node4 --shard-id shard1 --zone-id zone1

Snapshot restore & consistency note

  • When starting a brand-new cluster from a snapshot, a strict majority of the nodes listed in --initial_voters must be restored with the exact same snapshot.
  • The remaining founding voters and all learners can start completely empty — they will safely download the snapshot from the leader once the cluster forms.
  • Empty nodes are never conflicting. Only divergent log histories cause issues.
  • New nodes not listed in --initial_voters start as learners and wait for the founding voters to elect a leader before catching up.

Service Discovery: Roomzin, SDKs and RZGate need to resolve node identities to network addresses. Roomzin supports Static (stable IPs) or HTTP (dynamic environments) discovery. You provide a simple HTTP endpoint — no embedded Consul, Kubernetes, or Istio dependencies. This keeps Roomzin lightweight and infrastructure-agnostic. Click on the Discovery link on sidebar to see more details

See Discovery section

Bulk Data Loading & Migration

Need to bootstrap or migrate Gigabytes of existing data? See the Bulk Load section — Roomzin can ingest a pre-built binary snapshot at over 1 million records per second per node.

Build the snapshot offline from your current database, distribute it to the cluster, and start with all historical data instantly available.

Roomzin adapts to your infrastructure — not the other way around.

HTTP/JSON Access with RzGate


For platforms that need HTTP/JSON access instead of native SDK integration, Roomzin offers RzGate — a standalone HTTP proxy that sits between legacy clients and your Roomzin cluster.

RzGate converts REST/JSON calls to Roomzin's binary protocol with minimal overhead (~10–15%), making it ideal for:

  • Existing HTTP-based booking engines
  • Third-party systems and partners
  • Quick testing and staging environments
View full RzGate documentation →

Continue with native SDKs for production systems; use RzGate for legacy integration or temporary access.

Bulk Load into an Offline Snapshot

Roomzin enables efficient data initialization by creating snapshots directly from CSV files. This bulk loading method is significantly faster than traditional insert queries, capable of ingesting over 2.5 million records per second. Use the build-snapshot command to generate snapshots, which are automatically loaded from your configured data_dir at startup.

Important Consideration

This is a one-time initialization procedure for new shards. To minimize synchronization time with ongoing database updates, perform this load during low-traffic periods. Once complete and the live cluster is synchronized, this process does not need to be repeated.

Preparation

To create a snapshot, prepare these two CSV files and place them in a directory:

properties.csv

Defines properties with their details and attributes

packages.csv

Defines room types, availability, pricing, and rate cancellation details

Command

roomzin build-snapshot --shard-id <shard_id> --input-path /path/to/csvs --output-path /path/to/snapshot

Parameters

--shard-id
Unique shard identifier (e.g., "shard1")
--input-path
Directory containing properties.csv and packages.csv
--output-path
Directory to save snapshot files (should match data_dir in configuration)

CSV File Formats

properties.csv

PropertyID,Segment,Area,PropertyType,Category,Stars,Latitude,Longitude,Amenities
prop_1,segment_1,New York,hotel,test,4,40.713800,-74.005000,wifi|pool|gym|parking
PropertyID

UUID string (36 characters) or ASCII text (max 14 characters). Examples: "550e8400-e29b-41d4-a716-446655440000" or "prop_1"

Segment

Primary grouping key for data partitioning and sharding. Choose segments that evenly distribute your properties (e.g., geographic regions, business units, or logical groups). This is a required field in search queries and critically impacts performance.

Area

Secondary grouping for finer data organization within a segment. Use areas to further categorize properties (e.g., neighborhoods, districts) for more targeted searches.

PropertyType: Type (e.g., "hotel")
Category: Category (e.g., "test")
Stars: Star rating (e.g., 4)
Latitude/Longitude: Geographic coordinates
Amenities: Pipe-separated (|) list of amenities

packages.csv

PropertyID,RoomType,Date,Availability,FinalPrice,RateFeature
prop_1,room_1,2025-10-03,7,120,free_featurelation|non_refundable|pay_at_property|includes_breakfast
PropertyID

Matches a property in properties.csv

RoomType

Room type identifier (e.g., "room_1"; up to 256 types, recommended <10 for optimal performance)

Date: Date in short string format (e.g., "2025-10-03")
Availability: Number of available rooms (e.g., 7)
FinalPrice: Price for the room on that date (e.g., 120)
RateFeature: Pipe-separated (|) list of rate feature options

Next Steps

After running the command, place the generated snapshot files in the data_dir configured in roomzin.yml. Snapshots load quickly at startup, as demonstrated in our benchmarks.

Important Note

After Roomzin loads the seed snapshot, run one final delta sync to capture any transactions that arrived while the snapshot was being built. Use the SDK to stream these missing events.

Authentication

Token-based authentication with role-based access control (RBAC) for secure access to the admin API and system operations.

Authentication Overview

Roomzin uses token-based authentication with role-based access control (RBAC) for the admin API and certain actions. Tokens are defined in a separate YAML file specified in the server configuration (auth_file).

Authentication File Format

auth.yml
Sample authentication file
tokens:
  - token: abc123
    role: admin
  - token: c789
    role: client
  - token: mon456
    role: monitoring

Roles and Permissions

Detailed breakdown of access levels for each role

Admin
role: admin

Full system access to all endpoints and administrative commands including:

Shutdown
Update peers
TLS rotation
Save snapshot
Client
role: client

Full access to SDK commands (excluding administrative operations):

Includes all Monitoring role permissions

Monitoring
role: monitoring

Read-only access to system status endpoints:

/healthz
/metrics
/node-info
/leader
/peers (GET requests only)

Request Authorization

All admin API requests require a Bearer token in the Authorization header:

Authorization: Bearer abc123

Invalid or unauthorized tokens return 401 or 403 errors.

Dynamic Token Management

In clustered mode, tokens can be reloaded without restart via the /reload-tokens endpoint (admin role required).

Codecs

Schema definitions for rate features with performance-optimized limits.

Critical Considerations

  • Any change to codecs.yml will invalidate existing queries
  • Requires a full restart of the cluster or standalone instance
  • All existing snapshots will be dropped as they become invalid
  • Modifications should be treated as an admin-level operation

Codecs Overview

Performance Trade-off: The limits of 24 rate features may seem restrictive, but this is a deliberate trade-off for optimal performance - hardly a real cap in real life.

Schema Definition

The schema for codecs.yml:

codecs.yml
rate_features:
  - free_featurelation
  - non_refundable
  # ... up to 24 entries

Order Sensitivity: The order of entries is critical, as Roomzin uses this file for internal mapping.

File Placement

The codecs.yml file must be placed in the same directory as the main configuration file (roomzin.yml).

Configuration

Roomzin uses a YAML configuration file to define server settings and behavior.

Configuration File Location

The roomzin.yml file is automatically located in one of these paths (in order of priority):

1
Specified via the --config CLI argument
2
Current directory (./roomzin.yml)
3
System directory (/etc/roomzin/roomzin.yml)

The configuration includes sections for core allocation, TCP settings, cluster settings, authentication file path, API port, data directory, and maintenance scheduling. Below is a sample configuration with default values explained:

roomzin.yml
core_config:
   sys_cores_count: 0 
   # Cores dedicated to system (TCP/QUIC/routing/API). Default: 0 (auto ≈50% of total cores; rest go to processors)

tcp:
  listen_addr: "127.0.0.1"  # TCP listen address (default: "127.0.0.1")
  port: 7777  # TCP port for main service queries (default: 7777)
  max_connections: 10000  # Maximum concurrent connections (default: 10000)
  tcp_user_timeout_ms: 200  # TCP user timeout in milliseconds (default: 200)
  tcp_keepalive_seconds: 30  # TCP keepalive interval in seconds (default: 30)
  tcp_recv_buffer_size: 262144  # TCP receive buffer size in bytes (default: 256KB)
  tcp_send_buffer_size: 131072  # TCP send buffer size in bytes (default: 128KB)

cluster:
  # Node identity is set via CLI (--node-id, --shard-id, --zone-id)
  port: 17777  # Cluster port for node-to-node communication (default: 17777)
  cert_path: ""  # Path to TLS certificate file (required for clustered mode)
  key_path: ""  # Path to TLS private key file (required for clustered mode)
  ca_cert_path: ""  # Path to CA certificate file (required for clustered mode)
  discovery_kind: "static"  # Discovery mechanism: static or http (default: static)
  discovery_yml_path: "./discovery.yml"  # Path to discovery YAML (required for static discovery)
  discovery_http_host: ""  # HTTP host for discovery (required for http discovery)

auth_file: ""  # Path to authentication YAML file (required)
api_port: 0  # HTTP port for admin API (required; e.g., 8080)
data_dir: ""  # Path to data directory (required; must be writable)
maintenance_hour: 0  # Hour (0-23) for scheduled maintenance (default: 0)
max_dates_in_query: 14  # Maximum number of dates in search queries (default: 14)
query_limit: 200  # Maximum number of records in search results (default: 200)
snapshot_interval_sec: 60  # Raft snapshot interval in seconds (default: 60)
snapshot_trigger_count: 10000  # Raft WAL entries threshold for snapshot (default: 10000)
wal_flush_threshold: 2000  # WAL flush batch size (default: 2000)

Cluster Identity via CLI

The following cluster fields are not defined in the config file—they are provided via CLI:

  • --node-id – Unique node identifier
  • --shard-id – Shard identifier
  • --zone-id – Zone identifier
  • --initial-voters – Comma-separated list of founding voter nodes

This allows the same config file to be used across all nodes in a cluster.

Codecs Configuration

The codecs.yml file defines rate features like cancellation policies and room amenities. You can specify its location using the --codecs flag:

roomzin run --config ./config/roomzin.yml --codecs ./config/codecs.yml

If not specified, Roomzin looks for codecs.yml in the same directory as your config file.

Discovery Configuration

The discovery_kind setting determines how nodes find each other:

  • static – Requires discovery_yml_path to point to a YAML file with peer list
  • http – Requires discovery_http_host to point to an HTTP discovery service

The configuration is validated at startup to ensure the required fields are present for your chosen discovery method.

Configuration Notes

Core Allocation Configuration

sys_cores_count System Cores

Number of CPU cores dedicated to system tasks (TCP/QUIC handling, routing, API, serialization, locks). These cores are critical for low tail latency and stable throughput under high request rates.

Default: 0 (auto ≈50% of total cores, rounded down). The remaining cores are automatically assigned to state processors (search, filtering, availability checks).

Performance impact:
• Too few system cores → routing, serialization and wire writes become bottlenecks → increased tail latency.
• Too many system cores → fewer cores for processors → reduced search throughput and higher query latency.
• Sweet spot (typically 40–60%) depends on workload size and hardware — auto default works well for most deployments.

Core allocation is tuned internally for predictable performance. Defaults provide good balance for most use cases. Advanced users can override sys_cores_count after benchmarking on their specific hardware and dataset.

TCP Buffer Configuration

tcp_recv_buffer_size Receive Buffer Size

Size of the TCP receive buffer (in bytes) for incoming client requests. Larger values can improve throughput for high-volume workloads but increase memory usage. Default: 256KB.

tcp_send_buffer_size Send Buffer Size

Size of the TCP send buffer (in bytes) for server responses. A low value can bottleneck performance, especially for slow-reading clients that don't read responses from the network quickly. Default: 128KB.

Note: Set this based on your client's reading speed and dataset size. Larger datasets or slower clients may need a higher value. Test incrementally to avoid wasting memory while ensuring smooth data flow.

Important Considerations

The data directory must be validated as writable during startup and stores both snapshots and WAL data.

For clustered mode, provide valid TLS certificate paths for secure node-to-node communication.

snapshot_interval_sec and snapshot_trigger_count control Raft snapshot frequency to manage WAL growth and recovery time.

wal_flush_threshold controls the batch size for WAL flushes. Default: 2000 entries.

REST API


API Overview

Roomzin provides a comprehensive HTTP admin API for monitoring and cluster management. All endpoints require Bearer token authentication and return JSON responses unless otherwise specified.

Authentication

All API requests require Bearer token authentication in the Authorization header:

Authorization: Bearer your-token-here

Tokens are configured via the authentication system and grant different access levels based on roles.

Common Endpoints

Available in both standalone and clustered modes:

Endpoint Method Description Required Role Response Format
/healthz GET

Node health status

Standalone: "active" or "unavailable"
Clustered: "active_leader", "active_follower", or "unavailable"

monitoring+ Status + Text
/metrics GET

System metrics in Prometheus format

Standalone: Filters cluster-specific metrics
Clustered: Includes all system metrics

monitoring+ Prometheus Text
/save-snapshot POST

Trigger manual snapshot creation

Initiates immediate snapshot process. Returns conflict if already in progress.

admin JSON
/codecs GET

Retrieve system codecs

Returns configured rate features from codecs.yml

monitoring+ JSON
/shutdown POST

Graceful system shutdown

Request Body: {"confirm": true}
Must include confirmation flag for safety

admin JSON

Clustered-Only Endpoints

Available only when running in clustered mode:

Endpoint Method Description Required Role Response Format
/reload-tokens POST

Reload authentication tokens

Refresh authentication tokens from disk without restart

admin JSON
/node-info GET

Get detailed node information

Returns: leader_id, leader_url, shard_id, zone_id, node_id

monitoring+ JSON Object
/leader GET

Get current cluster leader

Returns URL of the current leader node

monitoring+ JSON
/add-member/{node_id} POST

Promote learner to voting member

Parameters: node_id - Target node identifier
Elevates learner node to full voting member with consensus participation

admin JSON
/remove-member/{node_id} POST

Demote voting member to learner

Parameters: node_id - Target node identifier
Converts voting member to learner role, maintaining data replication without voting rights

admin JSON
/transfer-leader/{node_id} POST

Initiate leadership transfer

Parameters: node_id - New leader candidate identifier
Requests Raft leadership transfer to specified node. Requires target to be up-to-date voting member

admin JSON
/tls-rotate POST

Rotate TLS certificates

Signal TLS certificate rotation for node-to-node QUIC communication

admin JSON
/peers GET

Get cluster peer list

Returns array of peer node addresses

monitoring+ JSON Array

Usage Examples

Basic Health Check

curl -H "Authorization: Bearer your-token" http://localhost:8080/healthz

Get Node Information

curl -H "Authorization: Bearer your-token" http://localhost:8080/node-info

Response Codes

  • 200 OK - Request completed successfully
  • 202 Accepted - Request accepted for processing (async operations)
  • 400 Bad Request - Invalid request parameters or missing confirmation
  • 401 Unauthorized - Missing or invalid authentication token
  • 403 Forbidden - Insufficient permissions for the requested endpoint
  • 409 Conflict - Operation cannot be performed due to current state
  • 500 Internal Server Error - Server encountered an error processing request
  • 503 Service Unavailable - Node is not ready to handle requests

Metrics Overview

Prometheus metrics for monitoring TCP server and cluster performance

TCP Server Metrics

Real-time monitoring of client connections and server performance

Active Connections Gauge
tcp_connections
Commands Processed Counter
tcp_commands_total
Bytes Received Counter
tcp_bytes_received_total
Bytes Sent Counter
tcp_bytes_sent_total
Server Errors Counter
tcp_errors_total
Client Errors Counter
tcp_client_errors_total
Client Disconnections Counter
tcp_client_disconnects_total
Failed Logins Counter
tcp_client_login_fail_total
Request Timeouts Counter
tcp_timeouts_total
Rejected Connections Counter
tcp_rejected_connections_total
Rejected Requests Counter
tcp_rejected_request_total

Cluster Metrics

Comprehensive cluster health and performance monitoring

Connection Metrics

Connections Opened Counter
cluster_connection_opened_total
Connections Accepted Counter
cluster_connection_accepted_total
Connections Rejected Counter
cluster_connection_rejected_total
Connection Errors Counter
cluster_connection_error_total
Connections Dropped Counter
cluster_connection_dropped_total

Request/Response Metrics

Messages Sent Counter
cluster_message_sent_total
Messages Received Counter
cluster_message_received_total
Bytes Sent Counter
cluster_bytes_sent_total
Bytes Received Counter
cluster_bytes_received_total
Send Errors Counter
cluster_send_error_total
Receive Errors Counter
cluster_receive_error_total

Condition Metrics

Leader Changes Counter
cluster_leader_changed_total

Snapshot Metrics

Snapshots Produced Counter
cluster_snapshot_produced_total
Snapshot Size Gauge
cluster_snapshot_size_bytes
Save Duration Gauge
cluster_snapshot_save_duration_ms
Write Pause Duration Gauge
cluster_snapshot_write_pause_duration_ms
Load Duration Gauge
cluster_snapshot_load_duration_ms
Snapshot Failures Counter
cluster_snapshot_failed_count_total
Snapshot Transfers Counter
cluster_snapshot_transfer_count_total
Transfer Duration Gauge
cluster_snapshot_transfer_duration_ms

Write-Ahead Logging (WAL) Metrics

WAL Flushes Counter
cluster_wal_flush_count_total
Last Applied WAL Index Gauge
cluster_last_applied_wal_index

Commands


Roomzin provides SDKs in Go, Java, Python, C#, and Node.js for querying and managing the in-memory inventory engine over TCP. SDKs handle automatic routing, failover, load balancing, and connection pooling. Select an SDK from the left menu to view its documentation.

Property Management

Set Property

Adds a new property or updates an existing one in the system

Room Package Management

Set Room Package

Sets availability, price, and rate features like cancellation policies for a room type on a date

Availability Management

Set Room Availability

Sets the exact availability for a room type on a specific date

Increment Room Availability

Increases the availability for a room type on a specific date

Decrement Room Availability

Decreases the availability for a room type on a specific date

Search & Query

Search for Availability

Queries the system based on search params like room type, dates, price, etc.

Search Properties

Searches properties by segment, area, type, or other criteria

Property Exists

Checks if a property exists in the system

Room Type Exists

Checks if a specific room type exists for a property

List Room Types

Lists all room types for a given property

List Dates of a Room Type

Lists dates set for one of a given property's room type

Get Room Day Details

Retrieves details for a room type on a specific date

List Segments

Lists all segments and their property counts

Delete Operations

Delete Day for a Room Type

Removes a room's package for a specific date

Delete Day for a Property

Removes all packages of a property on a specific date

Delete a Room Type

Removes a specific room type from a property with all its packages

Delete Property

Removes a property and cascade purges its packages

Delete Segment

Removes a segment with all properties within the segment

Command Categories

Property & Package
Search & Query
Delete Operations

SDKs & Integration

Why use an SDK?
Roomzin SDKs provide automatic routing, failover, load balancing, and connection pooling. They handle cluster topology changes, retry logic, and serialization — so you can focus on your business logic. All SDKs implement the same CacheClientAPI interface and support both standalone and clustered modes.

Go SDK

GitHub
go get github.com/m-javani/roomzin-go

Java SDK

GitHub
Maven: io.github.m-javani:roomzin-java:1.0.0

Python SDK

GitHub
pip install roomzin-py

Node.js SDK

GitHub
npm install roomzin-js

C# / .NET SDK

GitHub
dotnet add package Roomzin.Sdk

No SDK for your language? Use the HTTP Proxy

If your platform cannot use one of the native SDKs — or you're integrating with a legacy system — Roomzin provides RzGate, a lightweight HTTP/JSON proxy.

  • ✓ REST/JSON — works with any HTTP client
  • ✓ Separate scaling — run it alongside your cluster
Learn more about RzGate

Benchmarking Roomzin & RzGate

Measure Roomzin's performance with precision.
Comprehensive benchmarking suite for both Roomzin (TCP/binary protocol) and RzGate (HTTP/JSON proxy). Supports regular and spike load patterns with customizable queries.

Roomzin Benchmark

  • ✓ Direct TCP benchmarking – measures raw protocol performance
  • ✓ Search & Update – benchmark both read and write operations
  • ✓ Custom queries – use your own query.yml
  • ✓ CSV data generator – create realistic test data
  • ✓ Regular & Spike modes – sustained and burst load patterns

RzGate Benchmark

  • ✓ HTTP/JSON performance – complete end-to-end latency
  • ✓ TLS & HTTP/2 – includes secure communication overhead
  • ✓ Response validation – sample responses in http.json
  • ✓ Existing cluster – benchmarks running RzGate instances
  • ✓ ~10–15% overhead – minimal performance impact vs native

Quick Setup

curl -sSL https://raw.githubusercontent.com/m-javani/roomzin-bench/main/scripts/setup.sh | bash

One command downloads everything you need: binaries, config files, certificates, and data generation scripts. Just run and start benchmarking in minutes.

📄 For All Users

The downloaded zip includes a benchmark_guide.txt file with complete instructions. No Rust knowledge required.

🦀 For Rust Developers

Clone and build from source:
git clone https://github.com/m-javani/roomzin-bench

GitHub Repository → Download Latest Release

Complete documentation, examples, and source code available on GitHub

Service Discovery

The Challenge

Roomzin cluster nodes need to discover each other's network addresses to form a cluster. External components — SDKs and RZGate — also need to know how to reach Roomzin nodes.

However, different infrastructures may use different discovery mechanisms and certificate identity formats.

Rather than baking in support for every possible discovery system, Roomzin abstracts address resolution and leaves the implementation to you — the client team. This keeps Roomzin lightweight and your infrastructure choices unconstrained.

How Discovery Works

Static Discovery

Simple & Stable

Provide a static mapping of node identities to addresses in your configuration. The client resolves addresses once and never updates them.

Use when:

  • Cluster nodes have stable, predictable IPs or hostnames
  • Nodes are on bare metal or VMs with static addresses
  • You want zero external dependencies

HTTP Discovery

Dynamic & Flexible

Configure an HTTP endpoint that returns the current node-to-address mapping. Clients periodically fetch updates.

Use when:

  • Nodes are in Kubernetes with changing IPs
  • Auto-scaling groups add/remove nodes frequently
  • You want a single source of truth for addresses

HTTP Discovery API Specification

OpenAPI v3.0.0

If you choose HTTP discovery, you provide an API endpoint. Roomzin, RZGate, and SDKs will query it to resolve node identities to addresses. Each component requires a slightly different response format because internal node addresses may not be suitable for external clients.

discovery-api.yaml Implement these three GET endpoints
openapi: 3.0.0
info:
  title: Roomzin Discovery API
  version: 1.0.0
  description: |
    Implement these endpoints to let Roomzin components resolve node identities 
    to network addresses. The three endpoints return the same cluster information 
    but formatted for different consumers.

servers:
  - url: https://your-discovery-service.com
    description: Your discovery service endpoint

paths:
  /discovery/roomzin:
    get:
      summary: For Roomzin cluster nodes
      description: |
        Roomzin nodes use this endpoint when joining a cluster. Returns peer 
        information including TLS identity for secure node-to-node authentication.
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  version:
                    type: integer
                    description: Optional schema version for future compatibility
                  peers:
                    type: array
                    items:
                      type: object
                      required:
                        - node_id
                        - host
                        - identity
                        - port
                      properties:
                        node_id:
                          type: string
                          description: Roomzin node identifier
                        host:
                          type: string
                          description: IP address, hostname, or URL
                        port:
                          type: integer
                          description: Port for Roomzin communication
                        identity:
                          type: object
                          description: TLS identity for node authentication
                          properties:
                            kind:
                              type: string
                              enum: [dns, ip, spiffe]
                              description: Type of SAN used in the certificate
                            value:
                              type: string
                              description: The identity value (DNS name, IP address, or SPIFFE ID)
              example:
                version: 1
                peers:
                  - node_id: "node1"
                    host: "10.0.1.10"
                    port: 8443
                    identity:
                      kind: "ip"
                      value: "10.0.1.10"
                  - node_id: "node2"
                    host: "node2.roomzin.internal"
                    port: 8443
                    identity:
                      kind: "dns"
                      value: "node2.roomzin.internal"

  /discovery/rzgate:
    get:
      summary: For RZGate instances
      description: RZGate uses this endpoint to discover which Roomzin nodes to route requests to.
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  nodes:
                    type: array
                    items:
                      type: object
                      required:
                        - node_id
                        - addr
                      properties:
                        node_id:
                          type: string
                          description: Roomzin node identifier
                        addr:
                          type: string
                          description: Publicly accessible IP or hostname
                        port:
                          type: integer
                          description: Optional port (defaults to RZGate's configured port)
              example:
                nodes:
                  - node_id: "node1"
                    addr: "10.0.1.10"
                    port: 8080
                  - node_id: "node2"
                    addr: "10.0.1.11"
                    port: 8080

  /discovery/sdk:
    get:
      summary: For SDK clients
      description: |
        SDKs fetch this to connect directly to Roomzin nodes. 
        Separates TCP (data streaming) and API (control) ports.
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  nodes:
                    type: array
                    items:
                      type: object
                      required:
                        - node_id
                        - addr
                      properties:
                        node_id:
                          type: string
                          description: Roomzin node identifier
                        addr:
                          type: string
                          description: Publicly accessible IP or hostname
                        tcp_port:
                          type: integer
                          description: Port for TCP data streaming
                        api_port:
                          type: integer
                          description: Port for HTTP API requests
              example:
                nodes:
                  - node_id: "node1"
                    addr: "10.0.1.10"
                    tcp_port: 9090
                    api_port: 8080
                  - node_id: "node2"
                    addr: "10.0.1.11"
                    tcp_port: 9090
                    api_port: 8080

Port flexibility: Ports are included in the specifications to support different environments — such as tests or container port mappings — where the same node might be reachable on different ports. In production, all nodes typically use the same port for a given service.

Understanding TLS Identity

Roomzin uses QUIC for secure node-to-node communication. The QUIC server's TLS library automatically verifies certificates, but verification can fail if clients use different Subject Alternative Name (SAN) types.

The identity field tells Roomzin nodes how to verify each other's identities:

  • kind: "dns" — Certificate SAN contains DNS names, value is the DNS name
  • kind: "ip" — Certificate SAN contains IP addresses, value is the IP address
  • kind: "spiffe" — Certificate SAN contains SPIFFE IDs, value is the SPIFFE ID

Important: All nodes in the cluster must use the same kind to ensure successful TLS handshakes.

Response Format Comparison

/discovery/roomzin
{
  "peers": [{
    "node_id": "node1",
    "host": "10.0.1.10",
    "port": 8443,
    "identity": {
      "kind": "ip",
      "value": "10.0.1.10"
    }
  }]
}

Includes TLS identity for secure node authentication

/discovery/rzgate
{
  "nodes": [{
    "node_id": "node1",
    "addr": "10.0.1.10",
    "port": 8080
  }]
}

External address for request routing

/discovery/sdk
{
  "nodes": [{
    "node_id": "node1",
    "addr": "10.0.1.10",
    "tcp_port": 9090,
    "api_port": 8080
  }]
}

External addresses with separate data and API ports


Frequently Asked Questions

Everything you need to know about Roomzin's architecture and performance

What is Roomzin?


A fast and light inventory engine built for booking platforms.

Run it as a single instance behind your backend nodes, or spin up a cluster when you need HA and speed through horizontal scaling.

What do you mean by light?

  • One self-contained binary (<5 MB) handles single mode, cluster mode, and seed-data ingest. No extra tools or dependencies.
  • Feed it 5 GB of data and it uses ~2.5 GB RAM.
  • Its portable snapshot on disk is even tinier.

What do you mean by fast?
Under heavy traffic it answers complicated search queries with big payloads in a few milliseconds, beating every other tool.

Is that all?
Of course not. It’s also simple: simple to deploy, simple to use.



Explain the context


Planet-wide sharding
Global booking platforms serve the entire planet by dividing it into regional shards. Traffic is routed to the relevant shard, where multiple nodes provide high availability and load balancing. Each node stores only the inventory for its specific zone..

Cache is mandatory—yet painful
A cache layer sits in front of the database to hide disk latency. Sounds easy—until you discover that a cache is just a key-value bucket. Teams must design keys, set TTLs, invalidate on every update, and still make dozens of round-trips to collect scattered bits and join them in memory.

The ugly query
And that still doesn’t cover the hard query: “Find me two rooms, under $120 a night, breakfast included, pay-at-property, cancellable, for five nights, in a 4-star business hotel or motel, pet-friendly, with pool, gym, restaurant and a view, somewhere near this lat/lng.” The filter list is 100 k properties long; availability changes by the second; price changes by the minute.

DB fallback = latency killer
Fall back to the database? Latency murders high-TPS targets. Replicate for HA? More boxes, more shards, more midnight pages, more budget.

Roomzin steps in here
Keep reading to see where Roomzin slips into this picture and makes the noise quiet.



How Roomzin fits in?


Seat at the table
Roomzin parks right next to your cache, on top of the DB. One round-trip query—faster than any in-memory DB—returns every search hit for the hot window.

  • Full-blown DB? Nope. It accelerates only the booking hot-window (next weeks/months) that absorb ~95 % of live traffic.
  • Window cap? None hard-coded. Admins keep only the interval worth RAM—usually 15-90 days.
  • Tail-latency guard-rails Admins can soft-cap “max nights” and “max results per query” so a 10 k/s TPS never drags MS into seconds; edge cases spill to the main DB.
  • Any constraints on data? Just one: Up to 24 rate features—like cancellation policies, included breakfast, or WiFi—must be defined in your codecs.yml file to use as package filters. This limit isn’t fixed by the product, but is set by companies to cover 95% of real‑world cases—a deliberate trade‑off for speed and simplicity.
  • Redis killer? No. Redis stays for static key/value chores; Roomzin adds travel-native schema and single-query speed for hot searches.

Traffic flow in 3 steps

  1. Backend fires one complex search to Roomzin.
  2. Roomzin replies with matching property IDs + nightly room details for the whole stay.
  3. Client scrolls → platform fetches static hotel info from Redis only for items in view.

Result: 95 % of queries finish in milliseconds, Redis keeps doing what it loves, and your DB breathes again.



How easy is it to deploy Roomzin?


Standalone
One binary, three YAML files (config + auth + codecs list).
Run it. Done.

Cluster
Same binary on every node; they self-discover, elect a leader, and wire themselves into a secure cluster.
You only supply:

  • a seed list (one node name is enough)
  • certificates (.pem files) for TLS

Scale? Add a node → cluster grows. Remove a node → cluster shrinks. Zero config change, zero downtime.



How to connect to Roomzin? Who routes the requests?


No custom routing layer needed.
Roomzin lives inside one shard; all traffic stays internal to that shard.

SDKs do the heavy lifting

  • Go, Java, Python, C#, Node.js
  • Topology-aware: give any single node name, the SDK downloads the cluster map
  • Writes and reads are auto-routed to the fastest node(s) with built-in load-balancing
  • Leader crash? New election? Node disappears? SDK reroutes instantly—no code change, no restart
  • Need HTTP? Use RzGate — the standalone HTTP/JSON proxy for legacy systems

Only infra requirement: a DNS resolver so cluster and SDKs can resolve node IPs. That’s it.



What does the schema look like?


Three primitives:

  • Property: segment · area · property id (uuid or short string) · type · category · stars · geo(lat+lon) · amenities
  • Room Type: standard . double . deluxe . suite . etc...
  • Daily Package: date · availability · final price · rate & features

Hot window (15-90 days or more) lives entirely in RAM; update or patch any field in real time while searches stay instant.

All commands/queries in SDKs are around these entities for millions of records



Start fresh or seed from existing DB?


Bulk seed in seconds
Point Roomzin at a CSV snapshot: it ingests > 2.5 M records/sec, zero INSERT queries.

One-time task per fresh shard
Run it once for a fresh shard; when the snapshot lands the cluster starts live-sync and never needs a repeat.

Final whisk
Stream the last delta (whatever arrived while this process) with SDK.



Is data partitioned across Roomzin nodes?


Nope. Every node keeps the entire state in RAM. The leader replicates each write to all followers; consistent state arrives in single-digit milliseconds—no sharding keys, no cross-node joins, no surprises.



Are writes instant under heavy load?


Yes. Read and write paths are decoupled; the write channel always gets priority, so even when search traffic floods the queue, updates still commit in milliseconds.



Does Roomzin partition data inside a node?


Segments = inner parallelism
Each node splits its copy of the shard into user-defined segments (think “micro-regions” or “search buckets”).

  • Every segment owns its own processing queue — thousands of Paris properties? Split into 5-10 segments and let all cores run in parallel.
  • No geography baked into IDs — devs choose segment keys that mirror real-world search patterns (city name, district, GeoHash, custom hash).
  • Segment is mandatory in every search—just like picking a city

Use it when you want to squeeze every last core; skip it when your data is already cozy.



How visible is Roomzin?


Prometheus-native
Every node exposes metrics in plain Prometheus format: TCP activity, cluster connectivity, request flow, system state, snapshots, WAL ops—Grafana-ready out of the box.

REST self-description
Each node also serves health check, node info, known peers, current role, known leader, last snapshot info, and quorum size—no guessing, just query.



Can the REST API do more than read?


Absolutely.

Admins also write: shutting down a node, update peers, trigger a snapshot, or rotate TLS certs—all without SSH or restart.



How does a node join the cluster?


  1. Discovery & Quorum
    Starts in “Joining” mode, replays its local WAL, then knocks on every seed address. Shard-ID and certificates must match; if the cluster already exists it learns the current leader, otherwise it jumps into the election game.
  2. State Sync
    Compares its snapshot with the leader’s. Missing bytes? Streams the newest snapshot from a same-zone buddy first, falls back to any node, then rebuilds the in-memory state.
  3. Log Catch-up
    Downloads the WAL segment that starts right after the snapshot, replays every entry, and keeps ingesting live writes until it’s byte-perfect with the leader.
  4. Go Active
    Status flips to “Active”; the node now votes, replicates, and serves reads like every other member—zero manual steps, zero downtime.


Auto-maintenance?


Pick a low-traffic hour to trigger the tasks (single node or whole cluster). Roomzin drops yesterday’s expired data, then snaps a fresh checkpoint—no manual cleanup, no extra scripts.



Roomzin Distributed Consensus with TiKV Raft


Roomzin implements distributed consensus using the TiKV Raft crate, a production-proven implementation derived from etcd's Raft algorithm and inspired by HashiCorp's Raft research:

  • Optimized failure detection with automatic node recovery and cluster reconfiguration
  • Leader-based replication with write propagation to majority quorum before client acknowledgment
  • Single-digit millisecond consensus latency with strong consistency guarantees
  • Battle-tested implementation evolved from etcd's core Raft implementation

Production-Proven Foundation

Our consensus layer builds upon the TiKV Raft implementation, a CNCF graduated project derived from etcd's battle-tested Raft algorithm with enhancements inspired by HashiCorp's Raft research. This foundation provides the same reliability used in large-scale distributed systems across multiple industries, ensuring robust distributed coordination for Roomzin's real-time collaboration features.



What protocol do clients speak?


A custom, binary protocol—zero text parsing, zero wasted bits, built to serve high QPS pipelines full without breaking a sweat.



How do nodes talk to each other?


QUIC + TLS with auto certificate rotation—encrypted by default, zero-handshake latency, fast stream replication, and safe across data-centers. (Just keep valid certs on every node.)



What if my platform only speaks HTTP?


Use RzGate — roomzin's standalone HTTP/JSON proxy. It sits between your legacy HTTP clients and Roomzin, converting REST/JSON calls to Roomzin's native protocol with only ~10–15% overhead. Perfect for existing booking engines, third-party systems, or quick testing without SDK integration.



Is TLS enabled on the client TCP port?


Nope. Roomzin sits inside your private mesh; leave TLS termination to the load-balancer or service mesh you already run.



Who can do what?


Three built-in roles, no extra plugins:

  • monitoring – read metrics & safe GET endpoints only
  • client – CRUD on TCP, but just GET on REST
  • admin – full control: writes, config, shutdown, snapshots, TLS rotate

Lock them down with sdk credentials or token headers — done.



What language is Roomzin built in?


100 % Rust—zero-GC, fearless concurrency, single 5 MB binary that ships everything it needs.



What kind of performance can I expect?


  • Resilient & predictable – under any connection mix, data volume, or traffic
  • Median latency – single-digit ms at million-record scale
  • Snapshots – < 1 s for millions of records
  • Reload – millions of records in (few) second(s)
  • Memory footprint – ≤ 50 % of raw data size


Recap on the key capabilities?


Built for booking-scale workloads

  • Any topology: single node → clustered
  • HA: leader/follower, WAL & snaps
  • Zero-ops: auto-discovery, join, catch-up
  • Secure wire: QUIC + TLS auto-rotate
  • RBAC & realtime metrics
  • Partitioned data for parallel speed
  • Hot snapshots under load in cluster mode
  • Smart SDKs: Java, Go, Python, C#, Node.js

Easy integration

  • SDKs: auto-route, failover, retry, one-liner install
  • Deployment: <5 MB binary, zero external deps
  • Load from DB: CSV seed at +2.5 M records/sec
  • HTTP access: RzGate proxy for legacy/REST clients