Skip to content

Tap30/ripple-go

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Ripple Logo

Ripple | Go

A high-performance, scalable, and fault-tolerant event tracking Go SDK for server-side applications.


Features

  • Zero Runtime Dependencies – Built entirely with Go standard library
  • Thread-Safe – Concurrent event tracking with mutex protection
  • Auto-InitializationTrack() automatically calls Init() if not yet initialized
  • Disposal Tracking – Disposed clients silently drop events; explicit Init() re-enables
  • Automatic Batching – Efficient event grouping with dynamic rebatching for optimal network usage
  • One-Shot Timer – Flush timer fires once per scheduling cycle, not on a repeating interval
  • Smart Retry Logic – Intelligent retry behavior based on HTTP status codes:
    • 2xx (Success): Clear storage, no retry
    • 4xx (Client Error): Drop events, no retry (prevents infinite loops)
    • 5xx (Server Error): Retry with exponential backoff, re-queue on max retries
    • Network Errors: Retry with exponential backoff, re-queue on max retries
  • Retry CancellationDispose() aborts in-flight retries via context cancellation
  • Event Persistence – Disk-backed storage for reliability
  • Pluggable Adapters – Custom HTTP and storage implementations

Installation

go get github.com/Tap30/ripple-go@latest

Or install a specific version:

go get github.com/Tap30/ripple-go@v0.0.1

Quick Start

Basic Usage

package main

import (
    ripple "github.com/Tap30/ripple-go"
    "github.com/Tap30/ripple-go/adapters"
)

func main() {
    client, err := ripple.NewClient(ripple.ClientConfig{
        APIKey:         "your-api-key",
        Endpoint:       "https://api.example.com/events",
        HTTPAdapter:    adapters.NewNetHTTPAdapter(),
        StorageAdapter: adapters.NewNoOpStorageAdapter(),
    })
    if err != nil {
        panic(err)
    }
    defer client.Dispose()

    // Set global metadata
    client.SetMetadata("userId", "123")
    client.SetMetadata("appVersion", "1.0.0")

    // Track events (auto-initializes on first call)
    client.Track("page_view", map[string]any{
        "page": "/home",
    }, nil)

    // Track with event-specific metadata
    client.Track("user_action", map[string]any{
        "button": "submit",
    }, map[string]any{
        "schemaVersion": "1.0.0",
    })

    // Manually flush
    client.Flush()
}

Configuration

type ClientConfig struct {
    APIKey         string         // Required: API authentication key
    Endpoint       string         // Required: Event collection endpoint
    APIKeyHeader   *string        // Optional: Header name for API key (default: "X-API-Key")
    FlushInterval  time.Duration  // Optional: Default 5s
    MaxBatchSize   int            // Optional: Default 10
    MaxRetries     int            // Optional: Default 3
    MaxBufferSize  int            // Optional: Max events in storage (0 = unlimited)
    HTTPAdapter    HTTPAdapter    // Required: Custom HTTP adapter
    StorageAdapter StorageAdapter // Required: Custom storage adapter
    LoggerAdapter  LoggerAdapter  // Optional: Custom logger adapter
}

Configuration validation:

  • FlushInterval must be positive if provided
  • MaxBatchSize must be positive if provided
  • MaxRetries must be non-negative if provided
  • MaxBufferSize must be positive if provided, and >= MaxBatchSize

Understanding MaxBatchSize vs MaxBufferSize

MaxBatchSize (default: 10) - Controls when events are sent

  • Triggers immediate flush when queue reaches this size
  • Determines how many events are sent in each HTTP request

MaxBufferSize (default: 0 = unlimited) - Controls how many events are stored

  • Limits total events persisted to storage
  • When limit is reached, oldest events are dropped (FIFO eviction)
  • Must be >= MaxBatchSize (returns error otherwise)

API

Client Methods

Init()

Initializes the client and restores persisted events. Uses double-checked locking for thread safety. Resets the disposed state, so calling Init() after Dispose() re-enables the client.

Note: Track() automatically calls Init(), so explicit initialization is optional.

Track(name string, payload map[string]any, metadata map[string]any) error

Tracks an event with optional payload and metadata.

Parameters:

  • name - Event name/identifier (required, cannot be empty)
  • payload - Event data payload (optional, pass nil if not needed)
  • metadata - Event-specific metadata (optional, pass nil if not needed)

Usage examples:

  • Track("page_view", nil, nil) - Simple event tracking
  • Track("click", map[string]any{"button": "submit"}, nil) - Event with payload
  • Track("purchase", payload, map[string]any{"version": "1.0"}) - Event with payload and metadata

If the client is disposed, events are silently dropped (returns nil). Otherwise, auto-calls Init() if not yet initialized.

SetMetadata(key string, value any)

Sets a metadata value that will be attached to all subsequent events.

GetMetadata() map[string]any

Returns a copy of all stored metadata. Returns empty map if no metadata is set.

GetSessionId() *string

Returns nil for server environments.

Flush()

Manually triggers a flush of all queued events.

Dispose()

Cleans up resources: aborts in-flight retries, clears queue, clears metadata, resets state. Does NOT flush events. Call Flush() before Dispose() if you want to send remaining events.

Close()

Alias for Dispose().

Advanced Usage

Custom HTTP Adapter

Implement the HTTPAdapter interface to use custom HTTP clients:

import (
    "context"
    "github.com/Tap30/ripple-go/adapters"
)

type MyHTTPAdapter struct{}

func (a *MyHTTPAdapter) Send(endpoint string, events []adapters.Event, headers map[string]string) (*adapters.HTTPResponse, error) {
    return a.SendWithContext(context.Background(), endpoint, events, headers)
}

func (a *MyHTTPAdapter) SendWithContext(ctx context.Context, endpoint string, events []adapters.Event, headers map[string]string) (*adapters.HTTPResponse, error) {
    // custom HTTP logic
    return &adapters.HTTPResponse{Status: 200}, nil
}

Custom Storage Adapter

import "github.com/Tap30/ripple-go/adapters"

type RedisStorage struct{}

func (r *RedisStorage) Save(events []adapters.Event) error { return nil }
func (r *RedisStorage) Load() ([]adapters.Event, error)    { return nil, nil }
func (r *RedisStorage) Clear() error                       { return nil }

Graceful Shutdown

sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)

go func() {
    <-sigChan
    client.Flush()
    client.Dispose()
    os.Exit(0)
}()

Logger Adapters

Adapter Output Configurable Use Case
PrintLoggerAdapter Stdout Yes Development and debugging
NoOpLoggerAdapter None No Production (silent logging)

Log Levels

  • DEBUG: Detailed debugging information
  • INFO: General information messages
  • WARN: Warning messages (default level)
  • ERROR: Error messages
  • NONE: No logging output
import "github.com/Tap30/ripple-go/adapters"

client, err := ripple.NewClient(ripple.ClientConfig{
    // ... other config
    LoggerAdapter: adapters.NewPrintLoggerAdapter(adapters.LogLevelDebug),
})

Storage Adapters

Adapter Capacity Persistence Use Case
NoOpStorageAdapter N/A None Default, no persistence
Adapter Capacity Persistence Use Case
---------------------- --------- ----------- ---------------------------------
NoOpStorageAdapter N/A None Default, no persistence
import "github.com/Tap30/ripple-go/adapters"

// No persistence (default)
storage := adapters.NewNoOpStorageAdapter()

For custom storage implementations (e.g., file, Redis, database), implement the StorageAdapter interface. See adapters/README.md for examples.

Concurrency Guarantees

  • Thread-Safe Flush: Multiple concurrent Flush() calls are serialized via mutex
  • Thread-Safe Init: Double-checked locking prevents race conditions during auto-init
  • Event Ordering: FIFO order is maintained even during retry failures
  • No Event Loss: Events tracked during flush are queued for the next batch

Error Handling

  • 2xx Success: Events cleared from storage
  • 4xx Client Errors: Events dropped (no retry)
  • 5xx Server Errors: Retried with exponential backoff (30s cap), re-queued on max retries
  • Network Errors: Same as 5xx

Architecture

  • Client – Public API, metadata management, disposal tracking
  • Dispatcher – Event batching, one-shot timer flushing, retry with context cancellation
  • Queue – Thread-safe FIFO event queue
  • MetadataManager – Thread-safe shared metadata
  • Adapters – Pluggable HTTP, storage, and logger implementations

See AGENTS.md for detailed architecture documentation.

Development

make test         # Run all tests
make test-cover   # Run tests with coverage
make fmt          # Format code
make lint         # Run linter
make build        # Build all packages
make check        # Run all CI checks

Playground

# Terminal 1: Start server
cd playground && make server

# Terminal 2: Run client
cd playground && make client

Guides

Design and API Contract

Read the Design and API Contract Documentation to learn about the framework-agnostic API contract for SDKs.

Contributing

See the contributing guide.

Uses Conventional Commits and automated semantic versioning.

License

Distributed under the MIT license.

About

Official Ripple SDK for Go.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

No packages published