Core: Core documentation without API reference
# Introduction
> Introduction to Actionbase
Actionbase is a database for serving user interactions in real time, production-proven across Kakao services. Follows, likes, recent views, related content — they are all **interaction data**. Every interaction is expressed in the same model — **who** did **what** to which **target** (*source → action → target*) — and the combination of source and target yields three axes: * **User–User (U2U)** — follow/unfollow, follower/following counts, timeline scans * **User–Item (U2I)** — likes/bookmarks, view history, bidirectional counters * **Item–Item (I2I)** — related products, similar-content graphs, and other precomputed item-to-item relations Actionbase materializes read-optimized structures at write time. Reads use bounded access patterns (GET, COUNT, SCAN) without expensive computation. When backed by HBase, Actionbase inherits durability and horizontal scalability. ## Focus [Section titled “Focus”](#focus) | Focuses on | Explicitly avoids | | --------------------------------------------- | ------------------------------------ | | Real-time interactions across U2U / U2I / I2I | General-purpose graph queries | | Bounded access patterns (GET, COUNT, SCAN) | Unbounded traversal or analytics | | Continuous writes, immediate reads | Batch ingestion or deferred indexing | | WAL/CDC to Kafka (yours or ours) | Owning downstream processing | | Pluggable storage (HBase now, others planned) | Building yet another storage engine | ## Storage Backend [Section titled “Storage Backend”](#storage-backend) Actionbase currently uses HBase as its primary storage backend. Lighter backends are [planned](https://github.com/kakao/actionbase/blob/main/ROADMAP.md#exploring) for smaller deployments. ## Production Usage [Section titled “Production Usage”](#production-usage) Used at Kakao services—primarily [KakaoTalk Gift](https://gift.kakao.com/home)—handling over a million requests per minute. Running in stable production for years. ## Next Steps [Section titled “Next Steps”](#next-steps) * [Quick Start](/quick-start/) — Try Actionbase in minutes * [Core Concepts](/design/concepts/) — Understand the design
# Quick Start
> Run Actionbase and try core operations in minutes.
 ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * Docker ## Start Actionbase [Section titled “Start Actionbase”](#start-actionbase) ```bash docker run -it ghcr.io/kakao/actionbase:standalone ``` This runs the server in the background (port 8080) and the CLI in the foreground. ```plaintext actionbase> ``` ## Write Data [Section titled “Write Data”](#write-data) Load sample data using a preset (at `actionbase>` prompt): ```plaintext load preset likes ``` This creates a `likes` database/table and inserts 3 edges: ```plaintext │ 3 edges inserted │ - Alice → Phone │ - Bob → Phone │ - Bob → Laptop ``` ```plaintext Alice --- likes ----> +--------+ | Phone | Bob ----- likes ----> +--------+ | | +--------+ +-- likes ----> | Laptop | +--------+ ``` At write time, Actionbase **precomputes everything** for reads—counts, indexes, ordering for **both directions**. This means you can instantly query: “What did Bob like?” (direction: OUT) “Who liked Phone?” (direction: IN) Counts and indexes ready at write time—no computation at query time. ## Read Data [Section titled “Read Data”](#read-data) ### Get [Section titled “Get”](#get) Check if a specific edge exists: ```plaintext get --source Alice --target Phone ``` ```plaintext │ The edge is found: [Alice -> Phone] │ |---------------|--------|--------|---------------------------| │ | VERSION | SOURCE | TARGET | PROPERTIES | │ |---------------|--------|--------|---------------------------| │ | 1737377177245 | Alice | Phone | created_at: 1737377177245 | │ |---------------|--------|--------|---------------------------| ``` ### Scan [Section titled “Scan”](#scan) **What did Bob like?** (direction: OUT) ```plaintext scan --index recent --start Bob --direction OUT ``` ```plaintext │ The 2 edges found (offset: -, hasNext: false) │ |---|---------------|--------|--------|---------------------------| │ | # | VERSION | SOURCE | TARGET | PROPERTIES | │ |---|---------------|--------|--------|---------------------------| │ | 1 | 1737377177350 | Bob | Laptop | created_at: 1737377177350 | │ | 2 | 1737377177297 | Bob | Phone | created_at: 1737377177297 | │ |---|---------------|--------|--------|---------------------------| ``` **Who liked Phone?** (direction: IN) ```plaintext scan --index recent --start Phone --direction IN ``` ```plaintext │ The 2 edges found (offset: -, hasNext: false) │ |---|---------------|--------|--------|---------------------------| │ | # | VERSION | SOURCE | TARGET | PROPERTIES | │ |---|---------------|--------|--------|---------------------------| │ | 1 | 1737377177297 | Bob | Phone | created_at: 1737377177297 | │ | 2 | 1737377177245 | Alice | Phone | created_at: 1737377177245 | │ |---|---------------|--------|--------|---------------------------| ``` ### Count [Section titled “Count”](#count) **How many items did Alice like?** (direction: OUT) ```plaintext count --start Alice --direction OUT ``` ```plaintext │ |-------|-----------|-------| │ | START | DIRECTION | COUNT | │ |-------|-----------|-------| │ | Alice | OUT | 1 | │ |-------|-----------|-------| ``` **How many users liked Phone?** (direction: IN) ```plaintext count --start Phone --direction IN ``` ```plaintext │ |-------|-----------|-------| │ | START | DIRECTION | COUNT | │ |-------|-----------|-------| │ | Phone | IN | 2 | │ |-------|-----------|-------| ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * [Core Concepts](/design/concepts/) — How Actionbase works * [Build Your Social Media App](/guides/build-your-social-media-app/) — Hands-on guide * [CLI Reference](/operations/cli/) — Full CLI documentation
# Core Concepts
> Core concepts behind Actionbase design
Actionbase is a database for serving user interactions—not a general-purpose graph database. ## Design Goals [Section titled “Design Goals”](#design-goals) * **Write-Time Optimization** — Pre-compute read structures at write time. Reads become simple lookups. * **Leverage Proven Storage** — Build on HBase for durability and scale. Don’t reinvent storage. ## Interaction Axes [Section titled “Interaction Axes”](#interaction-axes) Every interaction is expressed in the same model — **who** did **what** to which **target** (*source → action → target*). The combination of source and target yields three axes: * **User–User (U2U)** — follow/unfollow, follower/following counts, timeline scans * **User–Item (U2I)** — likes/bookmarks, view history, bidirectional counters * **Item–Item (I2I)** — related products, similar-content graphs, and other precomputed item-to-item relations They look different on the surface, but share the same structure: real-time access with predictable query patterns. ## Property Graph Model [Section titled “Property Graph Model”](#property-graph-model) Actionbase models interactions as edges: * **Source**: who or what acts (e.g., user\_id, product\_id) * **Target**: what is acted on (e.g., product\_id, content\_id, user\_id) * **Properties**: schema-defined attributes (e.g., `created_at`, `reaction_type`) ```plaintext User --[likes]--> Product (edge) ├─ source: "user123" ├─ target: "product456" └─ properties: { created_at: 1234567890, reaction_type: "heart" } User --[follows]--> User (edge) ├─ source: "user123" ├─ target: "user789" └─ properties: { created_at: 1234567891 } Product --[related]--> Product (edge) ├─ source: "product456" ├─ target: "product789" └─ properties: { score: 0.87 } ``` See [Schema](/design/schema/) for defining your edges. ## State and Event Model [Section titled “State and Event Model”](#state-and-event-model) Actionbase uses a state-based mutation model: * **State**: current state (e.g., “user liked product”) * **Event**: input that transitions state (e.g., “user clicked like”) When an interaction occurs: 1. Read current state 2. Apply state transition 3. Store new state Clients attach timestamps to events. Even if events arrive out of order, Actionbase computes the correct final state. See [Mutation](/design/mutation/) for details. ## Write-Time Optimization [Section titled “Write-Time Optimization”](#write-time-optimization) When an edge is written, Actionbase pre-computes: 1. **State** — current relationship between source and target 2. **Index** — ordered structures based on properties (e.g., `created_at DESC`) 3. **Count** — counters (e.g., number of likes per item) Reads use simple GET, COUNT, SCAN operations without query-time computation. See [Mutation](/design/mutation/) for how these are created. See [Query](/design/query/) for how to access them. ## Data Flow [Section titled “Data Flow”](#data-flow) ### Write Path [Section titled “Write Path”](#write-path) ```plaintext Client → Server → Engine → WAL → Storage → CDC ``` 1. Write to WAL for durability 2. Acquire lock 3. Read current state 4. Apply state transition 5. Compute indexes and counters 6. Write to storage 7. Emit CDC for downstream systems See [Mutation](/design/mutation/). ### Read Path [Section titled “Read Path”](#read-path) ```plaintext Client → Server → Engine → Storage → Response ``` * **COUNT** → EdgeCounter * **GET** → EdgeState * **SCAN** → EdgeIndex See [Query](/design/query/). ## Next Steps [Section titled “Next Steps”](#next-steps) * [Schema](/design/schema/): Define edge structure * [Guides](/guides/build-your-social-media-app/): Hands-on tutorial
# Glossary
> Key terminology used in Actionbase documentation
Key terms used in Actionbase documentation. ## Schema Version Mapping [Section titled “Schema Version Mapping”](#schema-version-mapping) v2 and v3 map almost 1:1. | v2 (Current) | v3 (Future) | Description | | ------------ | ----------- | ----------------------------- | | service | database | Namespace for grouping tables | | label | table | Edge schema definition | | src | source | Who performed the interaction | | tgt | target | What received the interaction | | ts | version | Timestamp for ordering | | fields | properties | Edge attributes | | dirType | direction | Direction type | | indices | indexes | Query indexes | | desc | comment | Description | * **Schema API**: v2 — migrating to v3 * **Query/Mutation APIs**: v3 ## Data Model [Section titled “Data Model”](#data-model) | Term | Description | | --------------- | --------------------------------------------------------------- | | **Edge** | A relationship representing a user interaction | | **Source** | Who — the entity performing the interaction (e.g., user\_id) | | **Target** | What — the entity receiving the interaction (e.g., product\_id) | | **Properties** | Attributes on an edge (e.g., `created_at`) | | **Unique-edge** | Edge identified by (source, target) pair | | **Multi-edge** | Edge identified by ID; multiple per (source, target) | | **Index** | Pre-computed structure for querying | | **Alias** | Alternative name for a label/table | ## Query and Mutation [Section titled “Query and Mutation”](#query-and-mutation) | Term | Description | | ------------- | ------------------------------------------------------------- | | **COUNT** | Query returning number of edges | | **GET** | Query retrieving edge by source and target | | **SCAN** | Query scanning edges using pre-computed index | | **Start** | Starting node for SCAN/COUNT; source when OUT, target when IN | | **Direction** | `OUT` (source→target) or `IN` (target←source) | | **Range** | Index-based scan boundaries at storage level | | **Filter** | Post-retrieval filtering at application level | | **Version** | Client timestamp (ms/ns) for concurrency and event ordering | | **Offset** | Encoded pagination position | | **Limit** | Max results (25 recommended) | ## Write-Time Optimization [Section titled “Write-Time Optimization”](#write-time-optimization) | Term | Description | | --------------- | ---------------------------------------- | | **EdgeState** | Current edge state, accessed by GET | | **EdgeIndex** | Sorted index entries, accessed by SCAN | | **EdgeCounter** | Pre-computed counters, accessed by COUNT | ## Data Pipeline [Section titled “Data Pipeline”](#data-pipeline) | Term | Description | | ------- | --------------------------------------- | | **WAL** | Write-Ahead Log for durability/recovery | | **CDC** | Change Data Capture for downstream sync |
# Mutation
> Understanding how mutations work in Actionbase
Mutations insert, update, and delete edges. The process ensures consistency, durability, and write-time optimization. See [Core Concepts](/design/concepts/) for background. ## Mutation Flow [Section titled “Mutation Flow”](#mutation-flow) ``` flowchart TD Request([Mutation Request with Event, Operation]) --> WAL[Write WAL] WAL --> Lock[Acquire Lock] Lock --> Read[Read State from storage] Read --> Modify{State exists?} Modify -->|Yes| ApplyEvent[Apply Event] Modify -->|No| InitialState[Initial State] InitialState --> ApplyEvent ApplyEvent --> ComputeAdditionalInfo[Compute Index, Count based on changed States] ComputeAdditionalInfo --> Write[Write State with AdditionalInfo to storage] Write --> Release[Release Lock] Release --> CDC[Write CDC] CDC --> Response([Response]) ``` ## Mutation Request [Section titled “Mutation Request”](#mutation-request) A mutation request contains: * **Event**: The data change (e.g., new property values, edge creation) * **Operation**: Insert, Update, or Delete ## Mutation Process [Section titled “Mutation Process”](#mutation-process) ### 1. Write WAL [Section titled “1. Write WAL”](#1-write-wal) Mutation is written to WAL before changes are made. Enables recovery and replay. In production, Kafka is used as WAL backend. ### 2. Acquire Lock [Section titled “2. Acquire Lock”](#2-acquire-lock) Prevents concurrent modifications: * **Unique edges**: Lock on (source, target) * **Multi edges**: Lock on edge ID ### 3. Read State [Section titled “3. Read State”](#3-read-state) Read current state from storage—properties, timestamps, metadata. ### 4. Apply Event [Section titled “4. Apply Event”](#4-apply-event) Transition state based on operation and client timestamp. See [State Transitions](#state-transitions) for details. ### 5. Compute Indexes and Counters [Section titled “5. Compute Indexes and Counters”](#5-compute-indexes-and-counters) Based on changed state: * **Indexes**: Delete old, create new * **Counters**: Increment or decrement ### 6. Write to Storage [Section titled “6. Write to Storage”](#6-write-to-storage) State, indexes, and counters written atomically. ### 7. Release Lock [Section titled “7. Release Lock”](#7-release-lock) Lock released after write. ### 8. Write CDC [Section titled “8. Write CDC”](#8-write-cdc) Mutation recorded in CDC (Kafka in production). Resulting state available for downstream systems. ## State Transitions [Section titled “State Transitions”](#state-transitions) Edges transition between states based on operations (INSERT, DELETE). Each event carries a client timestamp, and Actionbase uses these timestamps to compute the correct final state—even for out-of-order arrivals and duplicate requests (idempotent). See [`State.transit`](https://github.com/kakao/actionbase/blob/main/core/src/main/kotlin/com/kakao/actionbase/core/state/StateExtensions.kt) for implementation. ### Diagram [Section titled “Diagram”](#diagram) ``` flowchart LR INITIAL[["INITIAL: No Edge"]] ACTIVE(["ACTIVE: Edge Exists"]) INACTIVE["INACTIVE: Edge Deleted"] INITIAL -->|"INSERT / +1"| ACTIVE INACTIVE -->|"INSERT / +1"| ACTIVE ACTIVE -->|"DELETE / -1"| INACTIVE ``` ### Example: Out-of-Order Events [Section titled “Example: Out-of-Order Events”](#example-out-of-order-events) Alice’s actions: like(t=100) → unlike(t=200) → like(t=300) Events arrive out of order: like(t=100) → like(t=300) → unlike(t=200) | # | Event Arrives | State | Count Change | Total | | - | -------------- | ---------------- | ------------ | ----- | | 1 | like (t=100) | INITIAL → ACTIVE | +1 | 1 | | 2 | like (t=300) | ACTIVE → ACTIVE | 0 | 1 | | 3 | unlike (t=200) | ACTIVE → ACTIVE | 0 | 1 | Final state: **ACTIVE**, count: **1** — same result regardless of arrival order. ## Write-Time Optimization [Section titled “Write-Time Optimization”](#write-time-optimization) During mutations, Actionbase pre-computes: | Structure | Purpose | Query Type | | ----------- | ------------------ | ---------- | | EdgeState | Current edge state | GET | | EdgeIndex | Sorted entries | SCAN | | EdgeCounter | Aggregated counts | COUNT | Reads use simple GET, COUNT, SCAN without query-time computation. ## Consistency Guarantees [Section titled “Consistency Guarantees”](#consistency-guarantees) | Mechanism | Guarantee | | ----------------- | ----------------------------------------- | | Locking | Prevents concurrent modifications | | Atomic Writes | State and indexes written together | | WAL | Durability and recovery | | Read-Modify-Write | Mutations based on latest state | | State Transitions | Correct final state despite event arrival | | Idempotency | Replay produces same result | ## Next Steps [Section titled “Next Steps”](#next-steps) * [Query](/design/query/): Read pre-computed data * [Mutation API](/api-references/mutation/): API reference
# Query
> Understanding how queries work in Actionbase
Queries retrieve data pre-computed during mutations. See [Core Concepts](/design/concepts/) for background. ## Pre-computed Structures [Section titled “Pre-computed Structures”](#pre-computed-structures) | Structure | Created During | Accessed By | | ----------- | -------------- | ----------- | | EdgeState | Mutation | GET | | EdgeIndex | Mutation | SCAN | | EdgeCounter | Mutation | COUNT | You specify the query type and index. Each query accesses structures prepared at write time. ## Query Types [Section titled “Query Types”](#query-types) ### GET [Section titled “GET”](#get) Retrieves edge state by source and target. **Use case**: “Has this user viewed this product?” **Processing**: 1. Construct EdgeState key from source and target 2. Return edge state **MGet**: * Multiple source or target IDs → multi-get * Max 25 edges per request * Patterns: 1 source with N targets, or M sources with 1 target ### SCAN [Section titled “SCAN”](#scan) Scans edges using a pre-computed index with range filtering and pagination. **Use case**: “Recent products viewed by this user” **Processing**: 1. Construct EdgeIndex key prefix from source, table, direction, index 2. Apply range filters 3. Scan index entries 4. Apply optional filters 5. Apply pagination (limit, offset) 6. Return matching edges **Index Requirement**: * Must specify which index to use * Index must be defined in schema ### COUNT [Section titled “COUNT”](#count) Returns the number of edges for a source node. **Use case**: “How many products has this user viewed?” **Processing**: 1. Construct EdgeCounter key from source, table, direction 2. Return pre-computed counter ## Query Flow [Section titled “Query Flow”](#query-flow) ``` flowchart TD Request([Query Request]) --> Route{Query Type} Route -->|GET| GetQuery[Get Query] Route -->|SCAN| ScanQuery[Scan Query] Route -->|COUNT| CountQuery[Count Query] GetQuery --> EdgeState[Read EdgeState] ScanQuery --> EdgeIndex[Read EdgeIndex] CountQuery --> EdgeCounter[Read EdgeCounter] EdgeState --> Response([Response]) EdgeIndex --> Response EdgeCounter --> Response ``` ## Index Ranges [Section titled “Index Ranges”](#index-ranges) SCAN queries can specify ranges to filter at storage level. | Concept | Description | | -------------- | ----------------------------------------------- | | Explicit Index | Must specify which index | | Operators | `eq`, `gt`, `lt`, `between` set scan boundaries | | Index Order | Ranges applied in field order | | Sort Direction | Operator meaning depends on ASC/DESC | ### Range vs Filter [Section titled “Range vs Filter”](#range-vs-filter) | Type | Level | Uses Index | Performance | | ------ | ----------- | ---------- | --------------- | | Range | Storage | Yes | Fast | | Filter | Application | No | After retrieval | ## Pagination [Section titled “Pagination”](#pagination) | Parameter | Description | | --------- | ---------------------------- | | offset | Encoded starting position | | limit | Max results (25 recommended) | | hasNext | More results available | ## Query Direction [Section titled “Query Direction”](#query-direction) | Direction | Description | Example | | --------- | -------------- | ------------------------- | | OUT | Outgoing edges | Products a user liked | | IN | Incoming edges | Users who liked a product | Separate indexes and counters maintained for each direction. ## Read Path [Section titled “Read Path”](#read-path) ```plaintext Client → Server → Engine → Storage → Response ``` 1. **Client**: Query via REST API 2. **Server**: Validate request 3. **Engine**: Construct key, retrieve data 4. **Storage**: Return EdgeState/EdgeIndex/EdgeCounter 5. **Response**: Return to client ## Next Steps [Section titled “Next Steps”](#next-steps) * [Guides](/guides/build-your-social-media-app/): Hands-on tutorial * [Query API](/api-references/query/): API reference
# Queue
> A partitioned message queue built on immutable edge tables
queue/v1 is a partitioned message queue built on an [immutable edge table](/design/schema/#immutable-edge-tables-type-immutable_indexed-v3-immutable_edge). Messages are ordered within a partition and parallel across partitions, with an explicit lifecycle: **poll → process → commit**. See the [Queue API Reference](/api-references/queue/) for endpoints. ## Architecture [Section titled “Architecture”](#architecture) A queue is an immutable edge table with a fixed schema and a fixed access pattern. Define a queue, and each part is exactly one thing on the underlying [immutable edge table](/design/schema/#immutable-edge-tables-type-immutable_indexed-v3-immutable_edge): | queue/v1 | Immutable edge table | | ------------------------------------------ | -------------------------------------------- | | namespace | database | | queue | table, type IMMUTABLE\_EDGE | | partition = `xxhash32(key) mod partitions` | source (LONG) | | message id | target (STRING) — a server-assigned ULID | | seq (order / due time) | property (LONG) — the single `seq ASC` index | | value | property (STRING) — opaque JSON payload | | enqueue | INSERT one index row (append, lock-free) | | poll (`seq > offset`, optional `until`) | bounded scan of the `seq ASC` index (OUT) | | commit (`seq <= offset`) | scan-delete over that index range | The backing table is append-only and index-only: an enqueue is a single index-row write, and a poll is a single bounded index scan. There is no per-message state, no ack flags, and no consumer registry. ## Partitioning [Section titled “Partitioning”](#partitioning) A message is routed by its `key`: ```plaintext partition = xxhash32(key) mod partitions ``` * Messages with the same key land in the same partition and are read back in `seq` order. * `partitions` is fixed at queue creation (default 30 = 2·3·5, whose divisors allow many balanced consumer-shard splits). * Consumers fan out by polling partitions `0 .. partitions-1` independently. ## Consumer Lifecycle [Section titled “Consumer Lifecycle”](#consumer-lifecycle) ```plaintext poll(partition, offset) → process the batch → commit(partition, offset = batch offset) ``` 1. **Poll** scans one partition forward by `seq`. `offset` is exclusive (`seq > offset`); the response returns the batch and its next `offset` (the highest `seq` seen). An optional `until` bound (inclusive) restricts a poll to due messages when `seq` encodes a due time. 2. **Process** the batch. 3. **Commit** up to the batch’s offset: every message with `seq <= offset` is deleted. There is no separate ack state; the delete is the committed position, and the same operation serves retention. Caution Commit deletes a prefix of the partition, so it assumes **one logical consumer per partition** (Kafka-style). Independent consumers sharing a partition would delete each other’s unprocessed messages. ## Delivery Semantics [Section titled “Delivery Semantics”](#delivery-semantics) * **At-least-once**: commit only after processing. If a consumer crashes between poll and commit, the next poll re-reads the uncommitted messages. * **Ordered within a partition**: polls return messages in `seq` order; there is no ordering across partitions. * **Ordering key**: `seq` is a client-supplied, increasing key (a timestamp, LSN, or sequence number). Uniqueness is not required; the server-assigned ULID `id` keeps same-`seq` messages distinct. * **Commit is a real delete**: after a commit, re-polling from the start of the partition does not return the committed prefix. * **No CDC**: immutable edge tables do not emit CDC; the queue itself is the log. ## Use Cases [Section titled “Use Cases”](#use-cases) A durable, partitioned, append-only log with a due-time filter — a log primitive, not a full message broker. | Use case | Fit | Notes | | ------------------------- | ------- | ----------------------------------------------------------------------------------------- | | Refresh / delay scheduler | Good | `until` withholds not-yet-due messages; commit clears fired ones | | Durable per-partition log | Partial | Replay by `offset`; retention is manual via commit (a delete), one consumer per partition | | Ack-based work queue | No | No per-message ack, visibility timeout, redelivery, or dead-letter | Not provided: message ack / redelivery, deduplication, independent consumer groups on one partition, and server-side long-poll or streaming. ## Next Steps [Section titled “Next Steps”](#next-steps) * [Queue API Reference](/api-references/queue/): Endpoints * [Schema](/design/schema/): Immutable edge tables * [Mutation API Reference](/api-references/mutation/): The underlying scan-delete primitive
# Schema
> Defining the structure of your interaction data
Schema defines the structure of interaction data. Before storing data, define how edges are structured, what properties they have, and how they can be queried. See [Core Concepts](/design/concepts/) for background. ## Schema Hierarchy [Section titled “Schema Hierarchy”](#schema-hierarchy) ```plaintext Service (v3: Database) ├── Label (v3: Table) │ ├── Schema (src, tgt, fields) │ └── Indices (v3: Indexes) └── Alias ``` * **Service** groups related Labels and Aliases * **Label** defines the schema for edges * **Alias** provides an alternative name for a Label ## Service (v3: Database) [Section titled “Service (v3: Database)”](#service-v3-database) A namespace that contains labels and aliases. | Property | Description | | -------- | -------------------------------------- | | name | Service identifier (e.g., `myservice`) | | desc | Description (v3: comment) | | active | Whether active | Example: an e-commerce service might contain labels for `likes`, `recent_views`, and `purchases`. ## Label (v3: Table) [Section titled “Label (v3: Table)”](#label-v3-table) Defines the schema for edges—src, tgt, fields, and indices. | Property | Description | | -------- | ----------------------------------------------------------- | | name | Format: `service.label` | | desc | Description (v3: comment) | | type | Label type (INDEXED, HASH, MULTI\_EDGE, IMMUTABLE\_INDEXED) | | schema | Edge structure (src, tgt, fields) | | dirType | Direction type (v3: direction) | | storage | Storage URI (e.g., `datastore:///
`) | | indices | Indices for querying (v3: indexes) | | active | Whether active | ### Schema Definition [Section titled “Schema Definition”](#schema-definition) * **src** (v3: source): source type (STRING, LONG) — who * **tgt** (v3: target): target type (STRING, LONG) — what * **fields** (v3: properties): each with name, type, nullable **Example: Recent Views** ```plaintext src: user_id (LONG) tgt: product_id (LONG) fields: - created_at (LONG) dirType: BOTH ``` **Example: Reactions** ```plaintext src: user_id (LONG) tgt: product_id (LONG) fields: - created_at (LONG) - reaction_type (STRING) dirType: BOTH ``` ### Index Definition [Section titled “Index Definition”](#index-definition) Indices enable efficient querying. Each index has a name and a list of fields with sort order. ```plaintext indices: - name: by_created_at fields: [created_at DESC] - name: by_type_and_time fields: [reaction_type ASC, created_at DESC] ``` Indices are pre-computed at write time. See [Query](/design/query/). ### Immutable Edge Tables (type: IMMUTABLE\_INDEXED, v3: IMMUTABLE\_EDGE) [Section titled “Immutable Edge Tables (type: IMMUTABLE\_INDEXED, v3: IMMUTABLE\_EDGE)”](#immutable-edge-tables-type-immutable_indexed-v3-immutable_edge) An append-only variant of the indexed label. A regular edge tracks mutable state (a like can be un-liked); an immutable edge records facts that never change, such as an event log or a message queue. Only index rows are persisted: there is no state row, no count records, and no caches. The index rows are the record. | Property | Constraint | | --------- | ----------------------------------------------- | | indices | At most one index | | dirType | Single direction (OUT or IN) — BOTH is rejected | | mutations | INSERT only — UPDATE and DELETE are rejected | These constraints ensure every persisted row is reachable by a single index scan, so deleting the scanned rows is a complete delete. That is what makes eviction (scan-delete) safe. Behavioral differences from a regular indexed label: | Operation | Behavior | | --------------- | ---------------------------------------------------------------------------------------------------------- | | Point get | Rejected; read with an index scan | | Count / `total` | Not supported; no count records are written | | CDC | Not emitted; the log itself is the change history | | Eviction | [Scan-delete](/api-references/mutation/#5-scan-delete-immutable-edge-tables) removes a scanned index range | **Example: Event Log** ```plaintext src: partition (LONG) tgt: event_id (STRING) fields: - seq (LONG) - payload (STRING) dirType: OUT indices: - name: seq_asc fields: [seq ASC] ``` Immutable edge tables back the [queue/v1 API](/design/queue/). ## Alias [Section titled “Alias”](#alias) An alternative name for a label. | Property | Description | | -------- | ------------------------- | | name | Format: `service.alias` | | desc | Description (v3: comment) | | target | The label it points to | | active | Whether active | Useful for gradual migrations or domain-specific naming. ## Naming Conventions [Section titled “Naming Conventions”](#naming-conventions) | Type | Format | Example | | ------- | ----------------- | ------------------- | | Service | Simple identifier | `myservice` | | Label | `service.label` | `myservice.likes` | | Alias | `service.alias` | `myservice.friends` | ## Schema Versions [Section titled “Schema Versions”](#schema-versions) v2 and v3 map almost 1:1. | v2 (Current) | v3 (Future) | | ------------ | ----------- | | service | database | | label | table | | src | source | | tgt | target | | ts | version | | fields | properties | | dirType | direction | | indices | indexes | | desc | comment | ## Next Steps [Section titled “Next Steps”](#next-steps) * [Mutation](/design/mutation/): Write data * [Query](/design/query/): Query data * [Metadata API](/api-references/metadata/): API reference
# Storage Backends
> Understanding storage backends in Actionbase
Actionbase abstracts storage through a minimal interface called `Datastore`. Different backends can be integrated. ## Required Operations [Section titled “Required Operations”](#required-operations) A storage backend must support: | Operation | Description | | -------------- | ------------------------------------------ | | get | Retrieve value(s) by key(s) | | delete | Delete a value by key | | scan | Range scan with prefix, start, stop, limit | | checkAndMutate | Atomic check-and-mutate for consistency | | batch | Batch mutations (optional, recommended) | ## Supported Backends [Section titled “Supported Backends”](#supported-backends) ### HBase [Section titled “HBase”](#hbase) Production backend. Distributed, scalable NoSQL on HDFS. | Characteristic | Description | | ---------------------- | ------------------------------------- | | Horizontal Scalability | Shards data across nodes | | Strong Durability | Data replicated across nodes | | Low-latency Access | Optimized for random reads and writes | HBase requires expertise when used directly (row key design, region splitting, cluster management). Actionbase provides a higher-level abstraction with interaction-specific features (State/Index/Count). See [HBase Operations](/operations/hbase/). ### Memory [Section titled “Memory”](#memory) In-memory backend for development and testing. | Characteristic | Description | | -------------- | ------------------------- | | Easy Setup | No configuration required | | No Persistence | Data lost on server stop | Ideal for local development and prototyping. ## How Actionbase Uses Storage [Section titled “How Actionbase Uses Storage”](#how-actionbase-uses-storage) | Actionbase Operation | Storage Operation | Data Structure | | -------------------- | ----------------- | --------------------- | | Get Query | get | EdgeState | | Scan Query | scan | EdgeIndex | | Count Query | get | EdgeCounter | | Mutation (lock) | checkAndMutate | Lock | | Mutation (write) | batch / put | State, Index, Counter | | Mutation (cleanup) | delete | Old indexes | ## Choosing a Backend [Section titled “Choosing a Backend”](#choosing-a-backend) | Backend | Use Case | | ------- | ----------- | | HBase | Production | | Memory | Development | Lighter backends are planned for smaller deployments. ## Next Steps [Section titled “Next Steps”](#next-steps) * [Data Encoding](/internals/encoding/): How data is encoded * [HBase Operations](/operations/hbase/): HBase configuration
# Community
> Join the Actionbase community — contribute, discuss, and collaborate
Welcome to the Actionbase community. Here you’ll find everything you need to participate in the project. [Contributing ](https://github.com/kakao/actionbase/blob/main/CONTRIBUTING.md)How to contribute code, docs, and translations [Governance ](https://github.com/kakao/actionbase/blob/main/GOVERNANCE.md)Project roles and decision-making [Release Policy ](https://github.com/kakao/actionbase/blob/main/RELEASES.md)Versioning scheme and support policy [Roadmap ](https://github.com/kakao/actionbase/blob/main/ROADMAP.md)Planned features and improvements [Code of Conduct ](https://github.com/kakao/actionbase/blob/main/CODE_OF_CONDUCT.md)Community standards and expectations [Security Policy ](https://github.com/kakao/actionbase/blob/main/SECURITY.md)How to report security vulnerabilities ## Contributors [Section titled “Contributors”](#contributors) [](https://github.com/kakao/actionbase/graphs/contributors)
# FAQ
> Frequently asked questions about Actionbase
## General Questions [Section titled “General Questions”](#general-questions) ### What is Actionbase? [Section titled “What is Actionbase?”](#what-is-actionbase) Actionbase is a database for serving user interactions at scale, spanning three axes — user–user (U2U), user–item (U2I), and item–item (I2I). It models every interaction the same way — **who** did **what** to which **target** — and materializes read-optimized structures at write time. Reads use bounded access patterns (GET, SCAN, COUNT). Actionbase is not a general-purpose graph database. It focuses on serving user interactions with predictable read patterns. ### Why is it called Actionbase? [Section titled “Why is it called Actionbase?”](#why-is-it-called-actionbase) The name reflects what it stores: user actions, modeled as interactions. ### What do “interactions” and “activities” mean in Actionbase? [Section titled “What do “interactions” and “activities” mean in Actionbase?”](#what-do-interactions-and-activities-mean-in-actionbase) An interaction captures a user action (like, view, follow) as an explicit **who → what → target** relationship with schema-defined properties. Internally, interactions are represented as edges in a graph. The terms “interaction” and “activity” are used interchangeably. ### What problems does Actionbase solve? [Section titled “What problems does Actionbase solve?”](#what-problems-does-actionbase-solve) * **U2U** — follow relationships, follower/following counts, timelines * **U2I** — likes, reactions, view history, and their counts * **I2I** — related products and similar-content relations Instead of reimplementing indexing, ordering, and counting logic per service, Actionbase pre-computes these at write time. ### When should I NOT use Actionbase? [Section titled “When should I NOT use Actionbase?”](#when-should-i-not-use-actionbase) * A single database instance handles your workload * You need general-purpose graph queries or traversals * Your team doesn’t have HBase operational experience * You’re not hitting scaling walls yet ### Is Actionbase production-ready? [Section titled “Is Actionbase production-ready?”](#is-actionbase-production-ready) Actionbase has been running in production at Kakao for years—serving Kakao services, primarily KakaoTalk Gift—handling over a million requests per minute. However, as an open source project, Actionbase is just getting started. Documentation for production deployment (Kubernetes, HBase operations) is still in progress. Early adopters should expect some rough edges. ### What is the history of Actionbase? [Section titled “What is the history of Actionbase?”](#what-is-the-history-of-actionbase) Development started at Kakao in 2023. After deployment to KakaoTalk Gift, it was open-sourced in January 2026 — see [Path to Open Source](/project/path-to-open-source/). ## Data Model [Section titled “Data Model”](#data-model) ### What data model does Actionbase use? [Section titled “What data model does Actionbase use?”](#what-data-model-does-actionbase-use) Actionbase uses a property graph model: * **Source**: who (e.g., user) * **Target**: what (e.g., product, content) * **Properties**: schema-defined attributes (e.g., `created_at`, `reaction_type`) Each interaction type (likes, views, follows) is defined as a separate table with its own schema. ### What is the difference between unique-edge and multi-edge? [Section titled “What is the difference between unique-edge and multi-edge?”](#what-is-the-difference-between-unique-edge-and-multi-edge) * **Unique-edge**: One edge per (source, target) pair. Identified by source and target. * **Multi-edge**: Multiple edges per (source, target) pair. Each edge has a unique `id`. Unique-edge fits likes, follows, recent views. Multi-edge fits cases like gift records where the same user can send multiple gifts to the same recipient. > **Note:** Current documentation focuses on unique-edge. Multi-edge documentation will be expanded later. ### Does Actionbase support vertices (entity data)? [Section titled “Does Actionbase support vertices (entity data)?”](#does-actionbase-support-vertices-entity-data) Currently, Actionbase focuses on edges (interactions). Entity data like user profiles or product information is typically stored elsewhere (e.g., RDB). Vertex support is planned for future releases. In the meantime, self-edges (source = target) can be used as a workaround for simple entity storage. ### Does Actionbase support schemas? [Section titled “Does Actionbase support schemas?”](#does-actionbase-support-schemas) Yes. Schemas define: * Source and target identifier types (int, long, string, etc.) * Interaction properties and their types Schemas determine which read-optimized structures (indexes, counts) are built at write time — see [Schema](/design/schema/) and [Metadata API](/api-references/metadata/). ### What are some example interaction models? [Section titled “What are some example interaction models?”](#what-are-some-example-interaction-models) **Recent views** * Source: `user_id` * Target: `product_id` * Properties: `created_at` **Reactions** * Source: `user_id` * Target: `content_id` * Properties: `created_at`, `reaction_type` ## Architecture [Section titled “Architecture”](#architecture) ### How does Actionbase handle writes? [Section titled “How does Actionbase handle writes?”](#how-does-actionbase-handle-writes) Actionbase processes interactions using a state-based [mutation](/design/mutation/) model: 1. Read current state 2. Apply incoming interaction as a state transition 3. Persist resulting state and read-optimized structures Even if events arrive out of order, the final state remains consistent. ### What is write-time optimization? [Section titled “What is write-time optimization?”](#what-is-write-time-optimization) Actionbase pre-computes State, Index, and Count structures when edges are written. This enables simple GET, COUNT, SCAN reads without query-time computation. 1. **State** — current relationship between source and target 2. **Index** — ordered structures based on properties (e.g., `created_at DESC`) 3. **Count** — counters (e.g., number of likes per item) [Core Concepts](/design/concepts/#write-time-optimization). ### How does Actionbase handle out-of-order events? [Section titled “How does Actionbase handle out-of-order events?”](#how-does-actionbase-handle-out-of-order-events) Clients attach timestamps to events. Actionbase uses these timestamps to compute the correct final state, regardless of arrival order — see [Mutation](/design/mutation/). ## Storage and Infrastructure [Section titled “Storage and Infrastructure”](#storage-and-infrastructure) ### What storage backends does Actionbase support? [Section titled “What storage backends does Actionbase support?”](#what-storage-backends-does-actionbase-support) Storage is abstracted in Actionbase. Any backend that meets the interface requirements can be plugged in. HBase is the current implementation; lighter backends are planned — see [Storage Backends](/design/storage-backends/). ### What HBase versions are supported? [Section titled “What HBase versions are supported?”](#what-hbase-versions-are-supported) Tested with HBase 2.4 and 2.5. No strict version requirements. ### Why use HBase? [Section titled “Why use HBase?”](#why-use-hbase) At Kakao, two storage options met the interface requirements at this scale: HBase and Redicoke (Kakao’s distributed KV store with Redis protocol). Both provide horizontal scalability, durability, and low-latency random reads/writes. We chose HBase because it was better suited for large data migrations via bulk loading. > **Note:** Bulk loading is part of the pipeline component. The initial open source release focused on Actionbase core; pipeline release is in progress. ## Streaming and Data Pipelines [Section titled “Streaming and Data Pipelines”](#streaming-and-data-pipelines) ### What are WAL and CDC? [Section titled “What are WAL and CDC?”](#what-are-wal-and-cdc) * **WAL (Write-Ahead Log)** — records incoming events as-is for replay and recovery * **CDC (Change Data Capture)** — records resulting state after mutation for downstream sync Both are accessible via Kafka consumers. ### How does Actionbase support analytics and pipelines? [Section titled “How does Actionbase support analytics and pipelines?”](#how-does-actionbase-support-analytics-and-pipelines) WAL and CDC streams can feed analytics systems, async processors, background jobs, or data migrations. ### Can Actionbase handle high write throughput? [Section titled “Can Actionbase handle high write throughput?”](#can-actionbase-handle-high-write-throughput) For high-frequency interactions (e.g., [recent views](/stories/use-cases/kakaotalk-gift-recent-views/)), Actionbase uses async processing via Spark Streaming: 1. Request queued to WAL, response returned immediately 2. Async processor (Spark Streaming) consumes queued WAL entries and sends mutations back to Actionbase 3. Mutations are throttled and applied in background This minimizes latency while sustaining throughput. Data is typically reflected within tens of milliseconds. Designed to remain stable even when traffic exceeds normal capacity. > **Note:** The pipeline component is currently internal. Open source release is in progress. ## Comparison [Section titled “Comparison”](#comparison) ### How does Actionbase compare to Neo4j? [Section titled “How does Actionbase compare to Neo4j?”](#how-does-actionbase-compare-to-neo4j) Actionbase is not a general-purpose graph database. * **Neo4j** — general-purpose graph queries, traversals * **Actionbase** — bounded access patterns (GET, SCAN, COUNT) for user interactions ### How does Actionbase compare to traditional RDBMS? [Section titled “How does Actionbase compare to traditional RDBMS?”](#how-does-actionbase-compare-to-traditional-rdbms) Actionbase is not a replacement for RDBMS. * **RDBMS** — general-purpose, transactional * **Actionbase** — specialized for user interactions at scale, with write-time materialization For most teams, a well-tuned RDBMS handles this fine. Actionbase exists for cases where that stopped being true. ## Use Cases [Section titled “Use Cases”](#use-cases) ### What are typical use cases for Actionbase? [Section titled “What are typical use cases for Actionbase?”](#what-are-typical-use-cases-for-actionbase) * Like buttons and reaction counts * “Recently viewed” lists * Follow/following feeds and follower counts * Related products and similar-content lists * Per-user interaction histories When these outgrow your RDBMS—sharding gets painful, caches drift—Actionbase can take over. ## Getting Started [Section titled “Getting Started”](#getting-started) ### What are the system requirements? [Section titled “What are the system requirements?”](#what-are-the-system-requirements) **Local (development)** — see [Quick Start](/quick-start/) * Java 17 * In-memory storage, no external dependencies **Production** — documentation in progress, see [Roadmap](https://github.com/kakao/actionbase/blob/main/ROADMAP.md) * Java 17 * 4 GB+ memory recommended (scales out horizontally) * Requires: HBase, Kafka, JDBC-compatible database for metadata (to be consolidated) ### How do I get started? [Section titled “How do I get started?”](#how-do-i-get-started) [Quick Start](/quick-start/). ### Do I need to set up HBase separately? [Section titled “Do I need to set up HBase separately?”](#do-i-need-to-set-up-hbase-separately) Yes — documentation in progress, see [Roadmap](https://github.com/kakao/actionbase/blob/main/ROADMAP.md). Lighter backends are planned for future releases. ### What programming languages are supported? [Section titled “What programming languages are supported?”](#what-programming-languages-are-supported) Actionbase provides a REST API. Any language that supports HTTP works — see [Query API](/api-references/query/) and [Mutation API](/api-references/mutation/). ## Contributing [Section titled “Contributing”](#contributing) ### How can I contribute? [Section titled “How can I contribute?”](#how-can-i-contribute) [Contributing](https://github.com/kakao/actionbase/blob/main/CONTRIBUTING.md).
# For RDB Users
> Understanding Actionbase from a relational database perspective
For users familiar with relational databases who want to understand how Actionbase fits alongside an RDB. ## Why Consider Actionbase? [Section titled “Why Consider Actionbase?”](#why-consider-actionbase) As services grow, tables storing user interactions—likes, recent views, follows, related items—often hit scaling walls: * Shard key management and hot entities * Cross-shard queries * Cache consistency Actionbase handles these by modeling interactions as **who** did **what** to which **target**, with write-time materialization on horizontally scalable storage. ## From Tables to Interactions [Section titled “From Tables to Interactions”](#from-tables-to-interactions) In an RDB, interaction data often lives in tables like: * `user_follows` (user–user) * `user_likes` (user–item) * `user_views` (user–item) * `product_related` (item–item) In Actionbase, these become edges: * **Source**: who or what acts (e.g., user\_id, product\_id) * **Target**: what is acted on (e.g., product\_id, user\_id) * **Properties**: schema-defined (e.g., `created_at`) Read-optimized structures (indexes, counts) are pre-computed at write time. ## When Actionbase Fits [Section titled “When Actionbase Fits”](#when-actionbase-fits) * Interaction tables dominate volume * Queries focus on listing or counting relationships * Sharding these tables gets painful ## Using Actionbase with an RDB [Section titled “Using Actionbase with an RDB”](#using-actionbase-with-an-rdb) Actionbase complements an RDB, not replaces it. A common pattern: 1. Transactional and domain data stays in RDB 2. Large-scale interaction data moves to Actionbase 3. Interaction queries served from Actionbase Start by migrating only the tables that present scaling challenges. ## Example: Mapping a Table [Section titled “Example: Mapping a Table”](#example-mapping-a-table) **RDB** ```sql CREATE TABLE user_product_wish ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id VARCHAR(255), product_id VARCHAR(255), created_at TIMESTAMP, visibility VARCHAR(50) ); ``` **Actionbase** ``` graph LR Alice((Alice)) -->|wishes| Phone((📱 Phone)) Alice -->|wishes| Laptop((💻 Laptop)) Bob((Bob)) -->|wishes| Phone ``` * **Source**: user\_id (STRING) * **Target**: product\_id (STRING) * **Properties**: `created_at` (LONG), `visibility` (STRING) The `id` column is not needed—unique-edges are identified by source and target. > **Note:** For multi-edge cases (e.g., multiple gifts from the same user to the same recipient), each edge requires a unique `id` — see [FAQ](/faq/#what-is-the-difference-between-unique-edge-and-multi-edge). Indexes for efficient queries: * `created_at DESC` — recent wishes * `visibility, created_at DESC` — filtered by visibility ## Next Steps [Section titled “Next Steps”](#next-steps) * [Schema](/design/schema/) — Define your edge schema * [Quick Start](/quick-start/) — Try Actionbase in minutes
# Build Your Commerce App with Live FOMO Counters
> Build FOMO-based commerce features using real-time aggregation
This guide will demonstrate how to build FOMO (Fear Of Missing Out) based commerce features using Actionbase’s **real-time aggregation** capabilities. ## Features to Implement [Section titled “Features to Implement”](#features-to-implement) * “N users viewing now” - real-time viewer count * “N users wished recently” - time-windowed wish count * Time-bucketed interaction aggregation (hourly, minutely, etc.) * Property-based aggregation ## Actionbase Capability: Real-Time Aggregation [Section titled “Actionbase Capability: Real-Time Aggregation”](#actionbase-capability-real-time-aggregation) Actionbase supports real-time aggregation of user interactions: * **Time buckets**: Aggregate by hour, minute, or custom intervals * **Discrete values**: Aggregate by property values * **Interaction counts**: Count user interactions in real time * **Property aggregation**: Sum, count, and other aggregations on edge properties This enables commerce features like live viewer counts and trending indicators without external caching or batch processing. ## Coming Soon [Section titled “Coming Soon”](#coming-soon) This guide will be available in a future release. Start with the [Build Your Social Media App](/guides/build-your-social-media-app/) guide to learn Actionbase’s core functionality first.
# Build Your Social Gifting App
> Build social-commerce gifting features using multi-edge
This guide will demonstrate how to build social-commerce gifting features using Actionbase’s **multi-edge** capabilities. ## Features to Implement [Section titled “Features to Implement”](#features-to-implement) * Gift sending and receiving history * Multiple gifts between the same users * Gift statistics and recommendations ## Actionbase Capability: Multi-Edge [Section titled “Actionbase Capability: Multi-Edge”](#actionbase-capability-multi-edge) By default, Actionbase edges are **unique-edge**—identified by (source, target) pairs. Multi-edge extends this model: * **Unique-edge**: One edge per (source, target) pair * **Multi-edge**: Multiple edges per (source, target) pair, each identified by a unique `id` Multi-edge is useful when you need to track multiple interactions between the same entities, such as gift records where the same user can send multiple gifts to the same recipient. ## Coming Soon [Section titled “Coming Soon”](#coming-soon) This guide will be available in a future release. Start with the [Build Your Social Media App](/guides/build-your-social-media-app/) guide to learn Actionbase’s core functionality first.
# Build Your Social Media App
> Build social features using Actionbase core functionality
This guide walks you through building a simple social media application using Actionbase. You will learn how to model and serve activity data for core features such as **follows**, **likes**, and **feeds**. [](https://github.com/kakao/actionbase/releases/download/examples/hero.webm) ## What You Will Build [Section titled “What You Will Build”](#what-you-will-build) By the end of this guide, you will have implemented: * **Follows** - Users can follow other users * **Likes** - Users can like posts * **Feed** - Display posts from followed users with real-time like counts These features demonstrate the core pattern behind most social applications. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * Docker * Web browser ## Start the Interactive Guide [Section titled “Start the Interactive Guide”](#start-the-interactive-guide) This guide has an interactive component that runs locally. 1. **Start Actionbase with Docker** ```bash docker run -it -p 9300:9300 ghcr.io/kakao/actionbase:standalone ``` 2. **Start the interactive guide** Once the CLI prompt (`actionbase>`) appears: ```plaintext guide start hands-on-social ``` 3. **Screen layout** You will see the screen with three panels: * **Left**: Progress sidebar showing guide steps * **Center**: Social app UI * **Right**: CLI terminal and API logs A popup will walk you through each step. If something goes wrong, click **Restart** in the top-left corner.  4. **Open in your browser** ```plaintext http://localhost:9300 ``` The sections below summarize what you’ll see — you can read through without running the guide, or use them as a reference while following along. ## Follow the Guide [Section titled “Follow the Guide”](#follow-the-guide) From here, continue in the **web browser**. The interactive guide walks you through each step. 1. **Welcome** Welcome to the Actionbase hands-on guide! In this tutorial, you will: * Build follows * Build likes * See your feed  2. **You are @zipdoki** You will play as **@zipdoki** throughout this tutorial. **Tip:** Press **Enter** to proceed.  3. **Set Up** First, let’s load **sample data** so you can focus on building. * Create database & tables * Add sample users & posts  4. **Load Sample Data** Click **Run** to create: * Database with users * Posts & likes tables ```bash load preset build-your-social-app ```  5. **Select Database** Switch to the `social` database. * Use database social ```bash use database social ```  6. **Explore the Data** In the previous step, we created these tables: * user\_posts — who posted what * user\_likes — who liked which post Browse around before we add new interactions.  7. **Follows** Let’s build a **follow** feature. * Create a table * Add a relationship * Query it  8. **Create Follows Table** Create a `user_follows` table. * Who follows whom  9. **Follow a User** Make **@zipdoki** follow **@j4rami**. This creates a connection between two users. * Precomputing count & index ```bash mutate user_follows --type INSERT --source zipdoki --target j4rami ... ```  10. **Check Follow Status** Verify the follow exists. * Query relationship ```bash get user_follows --source zipdoki --target j4rami ```  11. **Count Followers** Get **@j4rami**’s follower count. * No aggregation ```bash count user_follows --start j4rami --direction IN ```  12. **List Followers** Get the list of users following **@j4rami**. * Already indexed ```bash scan user_follows --start j4rami --index created_at_desc --direction IN ```  13. **Likes** Now let’s add **likes**. Same pattern as follows. * A user interacts with a post  14. **Like a Post** Make **@zipdoki** like **@j4rami**’s post. * Precomputing count & index ```bash mutate user_likes --type INSERT --source zipdoki --target 1 ... ```  15. **Check Like Status** Verify that **@zipdoki**’s like was recorded. * Query like status ```bash get user_likes --source zipdoki --target 1 ```  16. **And More** Just like follows, you can: * Count likes * List who liked a post Same pattern, same simplicity.  17. **Feed** Your **feed** now shows: * Posts from users you follow * Real like counts This is the core pattern behind most social apps.  18. **All Done!** You just built a **feed** with **follows** and **likes** — all powered by Actionbase. Now try it yourself: * Follow someone * Check your feed * Like a post  19. **Try It Yourself** Now it’s your turn to explore. The guide is complete, but the app is fully functional: * **Follow more users** - Search for users and build your network * **Check your feed** - See posts from people you follow * **Like posts** - Interact with content in your feed  ## Summary [Section titled “Summary”](#summary) You just built a feed with follows and likes - all powered by Actionbase. ### What You Learned [Section titled “What You Learned”](#what-you-learned) | Concept | Description | | ------------------ | -------------------------------------------------------------- | | **Tables** | Create tables with schemas, indexes, and bidirectional queries | | **Mutations** | Insert edges representing user interactions | | **Queries** | Get individual edges, count totals, and scan with indexes | | **Precomputation** | Counts and indexes are computed at write time for fast reads | ### Next Steps [Section titled “Next Steps”](#next-steps) * [Quick Start](/quick-start/) — Core operations in minutes * [CLI Reference](/operations/cli/) — Full CLI documentation * [Core Concepts](/design/concepts/) — How Actionbase works ### Share Your Feedback [Section titled “Share Your Feedback”](#share-your-feedback) Was this guide helpful? Did Actionbase’s concepts make sense? We’d love to hear your thoughts — questions, suggestions, or issues are all welcome. [Open an issue on GitHub](https://github.com/kakao/actionbase/issues) ### Behind the Scenes [Section titled “Behind the Scenes”](#behind-the-scenes) Curious why we built this guide? [Read the story](https://github.com/kakao/actionbase/issues/454).
# Encoding
> Internal data encoding and storage details
This document describes how Actionbase encodes and stores data internally. This information is primarily for contributors and those who need to understand the low-level storage format. For high-level concepts, see [Core Concepts](/design/concepts/). ## Row Types [Section titled “Row Types”](#row-types) Actionbase stores edge data using multiple row types in the storage backend. Each row type serves a specific query purpose. | Row Type | Type Code | Purpose | Query Type | | ---------- | --------- | ------------------ | ---------- | | Edge State | -3 | Current edge state | Get | | Edge Index | -4 | Index entries | Scan | | Edge Count | -2 | Edge counts | Count | Immutable edge tables (`IMMUTABLE_INDEXED`) persist Edge Index rows only, with no Edge State and no Edge Count. The index rows are the whole record, so point gets and counts are not supported and deleting a scanned index range is a complete delete. ## Edge State (Type Code: -3) [Section titled “Edge State (Type Code: -3)”](#edge-state-type-code--3) Stores the current state of edges for Get queries. ### Key Structure [Section titled “Key Structure”](#key-structure) ```plaintext [4-byte hash] + [1-byte + source] + [1-byte + table code] + [1-byte + type code(-3)] + [1-byte + target] ``` ### Value Structure [Section titled “Value Structure”](#value-structure) | Field | Type | Description | | ---------- | ------- | ----------------------------------------- | | active | Boolean | Edge active status | | version | Long | Edge version | | properties | Map | Property values with version per property | | createdAt | Long | Creation timestamp | | deletedAt | Long | Deletion timestamp | ## Edge Index (Type Code: -4) [Section titled “Edge Index (Type Code: -4)”](#edge-index-type-code--4) Stores index entries for Scan queries. Uses **Narrow Row** format for high-cardinality indexes. ### Key Structure [Section titled “Key Structure”](#key-structure-1) ```plaintext [4-byte hash] + [1-byte + directed source] + [1-byte + table code] + [1-byte + type code(-4)] + [1-byte + direction] + [1-byte + index code] + [(1-byte + N) * # index values] + [1-byte + directed target] ``` ### Value Structure [Section titled “Value Structure”](#value-structure-1) | Field | Type | Description | | ---------- | ---- | --------------- | | version | Long | Edge version | | properties | Map | Property values | ## Edge Count (Type Code: -2) [Section titled “Edge Count (Type Code: -2)”](#edge-count-type-code--2) Stores counters for Count queries. Uses the storage backend’s `increment` operation. ### Key Structure [Section titled “Key Structure”](#key-structure-2) ```plaintext [4-byte hash] + [1-byte + directed source] + [1-byte + table code] + [1-byte + type code(-2)] + [1-byte + direction] ``` ### Value Structure [Section titled “Value Structure”](#value-structure-2) | Field | Type | Description | | ----- | ---- | ----------- | | count | Long | Edge count | ## Row Key Encoding [Section titled “Row Key Encoding”](#row-key-encoding) All row keys follow a common pattern: ```plaintext [4-byte hash] + [1-byte + source] + [1-byte + table code] + [1-byte + type code] + [additional fields...] ``` | Component | Size | Purpose | | ------------- | -------- | --------------------------------- | | Hash | 4 bytes | xxhash32 for region distribution | | Type Code | 1 byte | Identifies data type (-2, -3, -4) | | Source/Target | Variable | Prefixed with 1-byte length | The hash prefix ensures even distribution across HBase regions, preventing hotspots. ## Value Encoding [Section titled “Value Encoding”](#value-encoding) Actionbase uses byte headers to maintain type information: ### Format [Section titled “Format”](#format) ```plaintext [1-byte type information] + [actual value] ``` ### Type Information [Section titled “Type Information”](#type-information) | Aspect | Description | | ---------- | -------------------------------------------------- | | Type Code | Distinguishes NULL, STRING, INT, FLOAT, JSON, etc. | | Sort Order | Includes ASC/DESC for index ordering | | Encoding | Values encoded according to sort order | ### Version Tracking [Section titled “Version Tracking”](#version-tracking) | Row Type | Version Scope | | --------- | -------------------- | | EdgeState | Version per property | | EdgeIndex | Version per edge | ## Key Design Principles [Section titled “Key Design Principles”](#key-design-principles) ### Hash Prefix [Section titled “Hash Prefix”](#hash-prefix) The 4-byte xxhash32 prefix ensures: * Even distribution across storage regions * Prevention of write hotspots * Balanced read/write load ### Negative Type Codes [Section titled “Negative Type Codes”](#negative-type-codes) Type codes use negative values (-2, -3, -4) to: * Separate from user data * Enable efficient key range scanning * Provide clear type identification ### Length-Prefixed Strings [Section titled “Length-Prefixed Strings”](#length-prefixed-strings) All variable-length fields use 1-byte length prefix: * Enables efficient parsing * Supports binary-safe encoding * Allows prefix-based scanning
# Benchmarks
> Performance benchmarks and metrics for Actionbase
Performance benchmarks for Actionbase. **Note:** At this initial release, benchmark results are approximate. We plan to provide transparent benchmarks using generalized datasets in future releases. ## Production Infrastructure [Section titled “Production Infrastructure”](#production-infrastructure) Actionbase runs as stateless services that can scale horizontally: * **Ingress** (Actionbase): 9 nodes (6 cores, 12GB RAM) — API gateway, request routing * **Worker** (Actionbase): 9 nodes (40 cores, 64GB RAM) — mutation processing, query execution * **HBase**: 9 region servers (20 cores, 64GB RAM each) — storage backend Ingress and Worker are Actionbase deployments. They maintain no local state—all data resides in the storage backend (HBase). This stateless architecture enables: * **Horizontal scaling**: Add nodes without data migration * **Rolling updates**: Deploy without downtime * **Failure recovery**: Any node can handle any request ## Production Traffic [Section titled “Production Traffic”](#production-traffic) Current peak: \~33k RPS (2M RPM). Typical workload: \~15% write, \~85% read. * **Latency**: Read \~5ms, Write \~20ms ## Capacity [Section titled “Capacity”](#capacity) 60k+ RPS in internal testing. ## Scalability [Section titled “Scalability”](#scalability) Linear within tested range.
# CLI
> Command-line interface for interacting with Actionbase
The Actionbase CLI provides an interactive console for managing databases, tables, and edges. It connects to an Actionbase server and supports all metadata and data operations. ## Installation [Section titled “Installation”](#installation) * Docker (Recommended) The easiest way to experience Actionbase is using Docker. The standalone image runs the server in the background and the CLI in the foreground: ```bash docker run -it --pull always ghcr.io/kakao/actionbase:standalone ``` This is sufficient for exploring Actionbase and understanding its core capabilities. * Homebrew ```bash brew tap kakao/actionbase https://github.com/kakao/actionbase brew install actionbase ``` * From Source ```bash git clone https://github.com/kakao/actionbase.git cd actionbase/cli make build ``` The binary is created at `cli/bin/actionbase`. ## Starting the CLI [Section titled “Starting the CLI”](#starting-the-cli) ```bash actionbase [options] ``` ### Startup Options [Section titled “Startup Options”](#startup-options) | Option | Description | Default | | ----------------- | ---------------------------------------------------- | ----------------------- | | `--host ` | Actionbase server URL | `http://localhost:8080` | | `--authKey ` | Authentication key | (none) | | `--debug` | Enable debug logging (shows HTTP requests/responses) | off | | `--plain` | Plain text output mode (no colors) | off | | `--proxy [port]` | Start in proxy mode for interactive guides | `9300` | | `--version` | Display CLI version and exit | | ### Examples [Section titled “Examples”](#examples) ```bash # Connect to local server actionbase # Connect to remote server actionbase --host https://actionbase.example.com # Enable debug mode to see HTTP traffic actionbase --debug # Start with proxy mode for guides actionbase --proxy ``` ## Context Management [Section titled “Context Management”](#context-management) The CLI maintains session context for database and table selection. The prompt displays the current context: ```plaintext actionbase> # No context actionbase(mydb)> # Database selected actionbase(mydb:mytable)> # Database and table selected ``` ### context [Section titled “context”](#context) Display current session state. ```plaintext context ``` **Output includes:** * Current host URL * Current database * Current table or alias * Proxy mode status (on/off with port) * Debug mode status (on/off) **Example:** ```plaintext actionbase> context │ |----------|--------------------------------| │ | KEY | VALUE | │ |----------|--------------------------------| │ | host | http://localhost:8080 | │ | database | likes | │ | table | likes | │ | alias | | │ | proxy | off (port -) | │ | debug | off | │ |----------|--------------------------------| ``` ### use [Section titled “use”](#use) Switch the current database, table, or alias context. ```plaintext use ``` | Subcommand | Description | | --------------------- | --------------------------------------------------------- | | `use database ` | Switch to specified database (clears table/alias context) | | `use table ` | Switch to specified table (requires database context) | | `use alias ` | Switch to specified alias (requires database context) | **Examples:** ```plaintext actionbase> use database likes │ Current database: likes actionbase(likes)> use table likes │ Current table: likes actionbase(likes:likes)> ``` ### debug [Section titled “debug”](#debug) Enable or disable debug mode. When enabled, HTTP requests and responses are logged. ```plaintext debug ``` **Example:** ```plaintext actionbase> debug on │ Debugging is on actionbase> get --source Alice --target Phone │ → GET /graph/v3/databases/likes/tables/likes/edges/get?source=Alice&target=Phone │ ← 200 OK {"edges":[...]} ``` ## Metadata Operations [Section titled “Metadata Operations”](#metadata-operations) ### create [Section titled “create”](#create) Create databases, storages, tables, or aliases. #### create database [Section titled “create database”](#create-database) ```plaintext create database --name --comment ``` | Flag | Required | Description | | ----------- | -------- | -------------------- | | `--name` | Yes | Database name | | `--comment` | Yes | Database description | **Example:** ```plaintext actionbase> create database --name social --comment "Social interactions" │ Database created: social ``` #### create storage [Section titled “create storage”](#create-storage) ```plaintext create storage --name --comment --storageType --hbaseNamespace --hbaseTable
``` | Flag | Required | Description | | ------------------ | -------- | ------------------- | | `--name` | Yes | Storage name | | `--comment` | Yes | Storage description | | `--storageType` | Yes | Storage type | | `--hbaseNamespace` | Yes | HBase namespace | | `--hbaseTable` | Yes | HBase table name | #### create table [Section titled “create table”](#create-table) ```plaintext create table --database --storage --name --comment --type --direction --schema [--indices ] [--groups ] ``` | Flag | Required | Description | | ------------- | -------- | ------------------------------- | | `--database` | Yes | Database name | | `--storage` | Yes | Storage name | | `--name` | Yes | Table name | | `--comment` | Yes | Table description | | `--type` | Yes | Table type | | `--direction` | Yes | Direction type (IN, OUT, BOTH) | | `--schema` | Yes | JSON schema definition | | `--indices` | No | JSON array of index definitions | | `--groups` | No | JSON array of group definitions | #### create alias [Section titled “create alias”](#create-alias) ```plaintext create alias --database --table
--name --comment ``` | Flag | Required | Description | | ------------ | -------- | ----------------- | | `--database` | Yes | Database name | | `--table` | Yes | Table name | | `--name` | Yes | Alias name | | `--comment` | Yes | Alias description | ### show [Section titled “show”](#show) Display databases, storages, tables, aliases, indices, or groups. ```plaintext show [--using
] ``` | Subcommand | Context Required | Description | | ---------------- | ---------------- | ------------------------------------------- | | `show databases` | None | List all databases | | `show storages` | None | List all storages with configuration | | `show tables` | Database | List tables in current database | | `show aliases` | Database | List aliases in current database | | `show indices` | Database + Table | Show indices for current or specified table | | `show groups` | Database + Table | Show groups for current or specified table | **Examples:** ```plaintext actionbase> show databases │ |-------|---------| │ | NAME | DESC | │ |-------|---------| │ | likes | Likes | │ | social| Social | │ |-------|---------| actionbase(likes)> show tables │ |-------|------|-----------| │ | NAME | TYPE | DIRECTION | │ |-------|------|-----------| │ | likes | ... | BOTH | │ |-------|------|-----------| actionbase(likes:likes)> show indices │ |------------------|--------| │ | NAME | FIELDS | │ |------------------|--------| │ | created_at_desc | ... | │ |------------------|--------| ``` ### desc [Section titled “desc”](#desc) Describe table or alias details including schema, fields, and indices. ```plaintext desc
[] ``` If `` is omitted, describes the current table or alias. **Example:** ```plaintext actionbase(likes)> desc table likes │ Table: likes │ Type: INDEXED │ Direction: BOTH │ │ Source: STRING │ Target: STRING │ │ Fields: │ |------------|------|----------| │ | NAME | TYPE | NULLABLE | │ |------------|------|----------| │ | created_at | LONG | false | │ |------------|------|----------| ``` ## Data Operations [Section titled “Data Operations”](#data-operations) ### get [Section titled “get”](#get) Query a specific edge by source and target. ```plaintext get [
] --source --target ``` | Flag | Required | Description | | ---------- | -------- | --------------------------------------------- | | `[table]` | No | Table or alias name (uses current if omitted) | | `--source` | Yes | Source node ID | | `--target` | Yes | Target node ID | **Example:** ```plaintext actionbase(likes:likes)> get --source Alice --target Phone │ The edge is found: [Alice -> Phone] │ |---------------|--------|--------|---------------------------| │ | VERSION | SOURCE | TARGET | PROPERTIES | │ |---------------|--------|--------|---------------------------| │ | 1737377177245 | Alice | Phone | created_at: 1737377177245 | │ |---------------|--------|--------|---------------------------| ``` ### scan [Section titled “scan”](#scan) Scan edges using an index. ```plaintext scan [
] --index --start --direction [--ranges ] [--limit ] ``` | Flag | Required | Description | | ------------- | -------- | --------------------------------------------- | | `[table]` | No | Table or alias name (uses current if omitted) | | `--index` | Yes | Index name to scan | | `--start` | Yes | Starting node ID | | `--direction` | Yes | Scan direction: IN, OUT, or BOTH | | `--ranges` | No | Range specification for filtering | | `--limit` | No | Maximum rows to return (default: 25) | **Example:** ```plaintext actionbase(likes:likes)> scan --index created_at_desc --start Alice --direction OUT │ The 2 edges found (offset: -, hasNext: false) │ |---|---------------|--------|--------|---------------------------| │ | # | VERSION | SOURCE | TARGET | PROPERTIES | │ |---|---------------|--------|--------|---------------------------| │ | 1 | 1737377177297 | Alice | Laptop | created_at: 1737377177297 | │ | 2 | 1737377177245 | Alice | Phone | created_at: 1737377177245 | │ |---|---------------|--------|--------|---------------------------| ``` ### count [Section titled “count”](#count) Count edges for a specific node and direction. ```plaintext count [
] --start --direction ``` | Flag | Required | Description | | ------------- | -------- | --------------------------------------------- | | `[table]` | No | Table or alias name (uses current if omitted) | | `--start` | Yes | Starting node ID | | `--direction` | Yes | Direction: IN, OUT, or BOTH | **Example:** ```plaintext actionbase(likes:likes)> count --start Alice --direction OUT │ |-------|-------| │ | DIR | COUNT | │ |-------|-------| │ | OUT | 2 | │ |-------|-------| actionbase(likes:likes)> count --start Phone --direction IN │ |-------|-------| │ | DIR | COUNT | │ |-------|-------| │ | IN | 2 | │ |-------|-------| ``` ### mutate [Section titled “mutate”](#mutate) Insert, update, or delete edges. ```plaintext mutate [
] --type --source --target --version --properties ``` | Flag | Required | Description | | -------------- | -------- | -------------------------------------------------- | | `[table]` | No | Table or alias name (uses current if omitted) | | `--type` | Yes | Mutation type: INSERT, UPDATE, or DELETE | | `--source` | Yes | Source node ID | | `--target` | Yes | Target node ID | | `--version` | Yes | Timestamp/version (supports `$NOW` placeholder) | | `--properties` | Yes | JSON object with edge properties (supports `$NOW`) | **Example:** ```plaintext actionbase(likes:likes)> mutate --type INSERT --source Charlie --target Phone --version $NOW --properties '{"created_at": $NOW}' │ Mutation result: 1 updated, 0 failed ``` ## Utility Commands [Section titled “Utility Commands”](#utility-commands) ### load [Section titled “load”](#load) Load and execute commands from a YAML file or preset. ```plaintext load [--ref ] ``` #### load file [Section titled “load file”](#load-file) Load commands from a local YAML file: ```plaintext load file ``` **YAML file format:** ```yaml - name: 'Create database' description: 'Optional description' command: "create database --name mydb --comment 'My database'" - name: 'Use database' command: 'use database mydb' ``` #### load preset [Section titled “load preset”](#load-preset) Download and execute a preset from the Actionbase GitHub repository: ```plaintext load preset [--ref ] ``` | Flag | Required | Description | | -------- | -------- | --------------------------- | | `` | Yes | Preset name | | `--ref` | No | Git branch or tag reference | **Available presets:** | Preset | Description | | ----------------- | ------------------------------------------------- | | `likes` | Sample likes data (Alice/Bob liking Phone/Laptop) | | `hands-on-social` | Social media application demo data | **Example:** ```plaintext actionbase> load preset likes │ 3 edges inserted │ - Alice → Phone │ - Alice → Laptop │ - Bob → Phone ``` ### guide [Section titled “guide”](#guide) Start an interactive guide. Requires proxy mode (`--proxy` flag at startup). ```plaintext guide start ``` **Available guides:** | Guide | Description | | ----------------- | --------------------------------------------- | | `hands-on-social` | Interactive social media application tutorial | **Example:** ```bash # Start CLI with proxy mode actionbase --proxy # Then start the guide actionbase> guide start hands-on-social ``` The guide opens a browser-based interactive tutorial that sends commands to the CLI. ### help [Section titled “help”](#help) Display all available commands with descriptions and usage. ```plaintext help ``` **Example:** ```plaintext actionbase> help │ Available commands │ |---------|-------------------------------|----------------------------------------| │ | NAME | DESCRIPTION | USAGE | │ |---------|-------------------------------|----------------------------------------| │ | context | Show current status | `context` | │ | create | Create database/storage/... | `create ` | │ | ... | ... | ... | │ |---------|-------------------------------|----------------------------------------| ``` ### exit [Section titled “exit”](#exit) Exit the CLI console. ```plaintext exit ``` You can also press `Ctrl+C` or `Ctrl+D` to exit. ## Common Workflows [Section titled “Common Workflows”](#common-workflows) ### Quick Start: Create and Query Data [Section titled “Quick Start: Create and Query Data”](#quick-start-create-and-query-data) ```plaintext # 1. Load sample data using preset actionbase> load preset likes # 2. Check current context (database and table are set automatically) actionbase(likes:likes)> context # 3. Query a specific edge actionbase(likes:likes)> get --source Alice --target Phone # 4. Scan all edges from Alice actionbase(likes:likes)> scan --index created_at_desc --start Alice --direction OUT # 5. Count edges actionbase(likes:likes)> count --start Phone --direction IN ``` ### Data Operations Workflow [Section titled “Data Operations Workflow”](#data-operations-workflow) ```plaintext # 1. Select database and table actionbase> use database social actionbase(social)> use table follows # 2. Insert an edge actionbase(social:follows)> mutate --type INSERT --source user1 --target user2 --version $NOW --properties '{"created_at": $NOW}' # 3. Verify the edge actionbase(social:follows)> get --source user1 --target user2 # 4. Delete the edge actionbase(social:follows)> mutate --type DELETE --source user1 --target user2 --version $NOW --properties '{}' ``` ### Interactive Learning with Guides [Section titled “Interactive Learning with Guides”](#interactive-learning-with-guides) ```bash # Start CLI with proxy mode enabled actionbase --proxy # Start the interactive guide actionbase> guide start hands-on-social ``` The guide opens in your browser and walks you through building a social media application. ## Reference [Section titled “Reference”](#reference) ### Direction Values [Section titled “Direction Values”](#direction-values) | Value | Description | | ------ | -------------------------------- | | `OUT` | Outgoing edges (source → target) | | `IN` | Incoming edges (target ← source) | | `BOTH` | Both directions | ### Mutation Types [Section titled “Mutation Types”](#mutation-types) | Type | Description | | -------- | ----------------------- | | `INSERT` | Create a new edge | | `UPDATE` | Update an existing edge | | `DELETE` | Delete an edge | ### Context Requirements [Section titled “Context Requirements”](#context-requirements) | Command | Database Required | Table Required | | ----------------- | ----------------- | ----------------- | | `create database` | No | No | | `create storage` | No | No | | `create table` | No | No | | `create alias` | No | No | | `show databases` | No | No | | `show storages` | No | No | | `show tables` | Yes | No | | `show aliases` | Yes | No | | `show indices` | Yes | Yes (or specify) | | `show groups` | Yes | Yes (or specify) | | `desc table` | Yes | No (uses current) | | `desc alias` | Yes | No (uses current) | | `get` | Yes | Yes (or specify) | | `scan` | Yes | Yes (or specify) | | `count` | Yes | Yes (or specify) | | `mutate` | Yes | Yes (or specify) | ### Special Features [Section titled “Special Features”](#special-features) **Multi-line input**: End a line with `\` to continue on the next line. ```plaintext actionbase> create table --database mydb --name mytable \ --comment "My table" --type INDEXED --direction BOTH \ --schema '{"src":{"type":"STRING"},"tgt":{"type":"STRING"}}' ``` **Quote handling**: Input automatically continues until quotes are balanced.
# HBase Configuration
> HBase setup and configuration guide for Actionbase
Actionbase uses HBase as its primary storage backend. This documentation will include setup instructions, configuration options, and operational best practices. * To try Actionbase, see [Quick Start](/quick-start/) * To contribute, see [Development Setup](https://github.com/kakao/actionbase/blob/main/CONTRIBUTING.md#development-setup)
# About Kakao
> Learn about Kakao, the company behind Actionbase
Kakao Corporation (카카오) is a major internet platform company in Korea. [KakaoTalk](https://en.wikipedia.org/wiki/KakaoTalk), its flagship messaging app, has [\~50 million monthly active users](https://t1.kakaocdn.net/kakaocorp/admin/ir/event/5835.pdf)—94% of Korea’s population. Actionbase was developed at Kakao to handle user interactions at this scale—likes, views, follows—across multiple services. It is now open source. ## Learn More [Section titled “Learn More”](#learn-more) * [Kakao Corporation](https://www.kakaocorp.com/) * [Kakao Tech Blog](https://tech.kakao.com/) * [Kanana (Kakao AI)](https://github.com/kakao/kanana)
# Path to Open Source
> Actionbase project development history before open source
This document covers the development history of Actionbase before it was open-sourced. Future development will be tracked in public repositories. ## MVP Phase [Section titled “MVP Phase”](#mvp-phase) The MVP phase represents the entire period from project inception to open-source release. During this period, Actionbase was applied to various services and evolved through real-world use. ### Phase 1: Foundation Building (2023) [Section titled “Phase 1: Foundation Building (2023)”](#phase-1-foundation-building-2023) **Q: How do we validate technical feasibility and establish a foundation?** This period was invested to ensure a technical base that would support stable development in subsequent phases. Key milestones: * Project kickoff and initial planning * Core design and implementation AI used: GPT-4 ### Phase 2: Service Integration (Early 2024) [Section titled “Phase 2: Service Integration (Early 2024)”](#phase-2-service-integration-early-2024) **Q: How do we validate through real-world service deployment?** The system was safely integrated into production by maintaining the existing system, applying dual writes, and gradually migrating read functionality after sufficient validation. Key activities: * First production service write/read migration: [KakaoTalk Gift Wish](/stories/use-cases/kakaotalk-gift-wish/) AI used: GPT-4 ### Phase 3: Stabilization (Late 2024) [Section titled “Phase 3: Stabilization (Late 2024)”](#phase-3-stabilization-late-2024) **Q: How do we stabilize the system and expand tenants?** Starting from a multi-tenant architecture, this phase introduced support for independent tenant configurations to ensure service isolation and enable isolated deployments. Key improvements: * System stabilization and tenant expansion * Support for independent tenant configurations to isolate services * Operations automation and monitoring enhancement * Applied across commerce domain (e.g., [KakaoTalk Gift Recent Views](/stories/use-cases/kakaotalk-gift-recent-views/)) AI used: Claude 3.5 Sonnet ### Phase 4: Building Sustainable Operations (Early 2025) [Section titled “Phase 4: Building Sustainable Operations (Early 2025)”](#phase-4-building-sustainable-operations-early-2025) **Q: How do we improve operational efficiency and address technical debt?** During this phase, Actionbase continued to be applied to various services while improving its operational foundation. Key efforts included reducing operational overhead, improving developer tooling, and addressing accumulated technical debt. Key activities: * Applied across multiple domains (e.g., [KakaoTalk Friends](/stories/use-cases/kakaotalk-friends/)) AI used: GPT-4o, Claude 3.5 Sonnet ### Phase 5: Preparing for Open Source (2025) [Section titled “Phase 5: Preparing for Open Source (2025)”](#phase-5-preparing-for-open-source-2025) **Q: How do we prepare for open-source release while expanding adoption?** Building on the foundation from Phase 4, this phase focused on making Actionbase ready for open source. The reimplementation process led to the decision to open-source Actionbase. While legacy code remains, the reimplementation continues to progress toward a more maintainable codebase. During this period, Actionbase was broadly adopted across [Kakao](/project/about-kakao/)’s services. Key changes: * Maintained API compatibility with existing systems * Redesigned API structure * Repository separation and organization * Separated internal and open-source areas AI used: GPT-4o, Claude 3.5 Sonnet, Cursor ## Open Source Release (January 2026) [Section titled “Open Source Release (January 2026)”](#open-source-release-january-2026) After this MVP period, Actionbase was open-sourced in January 2026. The codebase released is the same one that was developed, operated, and refined inside Kakao. Only minimal modifications were applied to remove security-sensitive or organization-specific details, and some internal features will be opened gradually as they are reviewed. ### Why We Open-Sourced Actionbase [Section titled “Why We Open-Sourced Actionbase”](#why-we-open-sourced-actionbase) The same interaction features—likes, recent views, follows—were being rebuilt across teams, each time with different stacks, different schemas, different failure modes. And when traffic grew, each hit similar scaling walls. Actionbase emerged as an attempt to stop solving the same problem ten different ways—and to solve it at scale. **Sharing a real journey** This codebase wasn’t designed for release. It grew through production incidents, shifting requirements, and years of real traffic. We chose to share it as-is—not a rewritten showcase, but the actual code that survived. **Outliving its creators** Internal systems often fade when their original maintainers move on. Open-sourcing creates a chance for the system to grow beyond any single team or company—shaped by those who actually use it. **Becoming part of something larger** Inside a company, an internal service is just another API to call. Developers designing new systems rarely adopt it as a core component, working with well-known technologies feels more valuable for their growth. We understand this. By open-sourcing Actionbase, we hope it can become part of architectures we never imagined. ### Was It the Right Call? [Section titled “Was It the Right Call?”](#was-it-the-right-call) Actionbase runs in production at over a million requests per minute—but proving something works is not the same as proving it was the right call. What you see here is what survived: scaling walls, live incidents with users waiting, and fixes shipped while the complaints were still coming in. We took the consolidation path, and scaling came with it. If you’ve rebuilt the same interaction features across teams, or scaled a single system until it broke, we’d like to learn from it—whether you faced the same fork, or found a different way entirely. ### Acknowledgements [Section titled “Acknowledgements”](#acknowledgements) This work was supported by Kakao’s leaders and engineers, who helped the team build and operate the system. Special appreciation goes to the HBase engineers at Kakao. Actionbase’s reliability benefits from the stability provided by HBase. This release reflects the efforts of those who contributed to the system and the teams who supported its development. We look forward to continuing this project together with the open-source community.
# Kubernetes Provisioning
> Kubernetes-based deployment guide for Actionbase
At Kakao, Actionbase is currently provisioned using Kubernetes. This documentation will include deployment manifests, high availability configurations, performance tuning, monitoring setup, and security best practices. * To try Actionbase, see [Quick Start](/quick-start/) * To contribute, see [Development Setup](https://github.com/kakao/actionbase/blob/main/CONTRIBUTING.md#development-setup)