Aerospike Go Client

repository·v8·Indexed 19 days ago

https://github.com/aerospike/aerospike-client-go

A high-performance, goroutine-friendly Go library for interacting with Aerospike databases. It implements the Aerospike wire protocol directly, providing capabilities for CRUD operations, administrative tasks via AdminCommand, and multiple authentication modes including Internal, External (LDAP), and PKI. The library includes a benchmark tool for tuning connection properties and supports build tags for Google App Engine and performance optimization.

Tokens
58.8K
Snippets
187
Records
267
Agent score
66%

What's inside aerospike-client-go

  1. Overview of Client Class methods

    v8
    The Client class provides the primary API surface for performing operations on an Aerospike cluster. Available methods include CRUD operations (Add, Get, Delete, Put), batch operations (BatchGet, BatchExists), User Defined Function (UDF) execution (ExecuteUDF, RegisterUDF), and querying/scanning capabilities.
  2. How CDT Context works for nested structures

    v8

    CDT (Complex Data Type) Context allows you to navigate deep into nested Maps and Lists. Instead of retrieving a whole large object to modify a small part, you provide a slice of *as.CDTContext to target a specific path.

    Context Navigation Types

    • as.CtxMapKey(value): Navigate to a map value by its key. Note that keys must be wrapped in an as.Value type (e.g., as.StringValue("key")).
    • as.CtxListIndex(index): Navigate to a list item by its integer index.
    • as.CtxMapRank(rank): Navigate to a map value by its rank.
    • as.CtxListRank(rank): Navigate to a list item by its rank.

    By chaining these contexts, you can reach deeply nested fields like user.profile.address.city or a specific item in a 2D array.

    // Example: Accessing a nested field 'zip' inside 'address' inside 'profile' inside 'user123'
    ctx := []*as.CDTContext{
        as.CtxMapKey(as.StringValue("user123")),
        as.CtxMapKey(as.StringValue("profile")),
        as.CtxMapKey(as.StringValue("address")),
    }
    
    record, err := client.Operate(nil, key,
        as.MapGetByKeyOp("data", "zip", as.MapReturnType.VALUE, ctx...),
    )
  3. Navigate nested CDT structures with Context helper functions

    v8

    To perform operations on nested Maps or Lists, use CDT Context helper functions to define the path to the target element. Common helper functions include:

    • CtxMapKey(key): Navigate to a specific map entry by its key.
    • CtxListIndex(index): Navigate to a specific list element by its index.
    • CtxMapRank(rank): Navigate to a map entry by its rank.
    • CtxListRank(rank): Navigate to a list element by its rank.

    For a complete list of available functions, consult the CDTContext documentation in the API reference.

  4. Optimize builds with build tags

    v8

    The Aerospike Go Client provides build tags to customize the binary for specific environments or performance requirements:

    • app_engine: Use this tag to build the library for Google App Engine. Note that aggregation functionality is not available in this build.
    • as_performance: Use this tag to remove the Reflection and Object APIs (methods with [Get/Put/...]Object names) from the build. This can help avoid accidental use of slower reflection-based APIs and potentially reduce binary size/complexity.
    # Example: Building for performance by removing reflection APIs
    go build -tags as_performance -o benchmark tools/benchmark/benchmark.go
    
    # Example: Building for Google App Engine
    go build -tags app_engine -o my_app main.go
  5. What are CDT Operations and how do they work?

    v8

    CDT (Complex Data Type) operations allow you to manipulate Maps and Lists stored in Aerospike bins directly on the server. Instead of the traditional read-modify-write cycle (which transfers the entire data structure over the network), CDT operations perform the logic on the Aerospike server.

    Key Benefits:

    • Reduced network traffic: Only the specific operation and result are sent, not the whole bin.
    • Atomicity: Operations are performed atomically on the server.
    • Performance: Improved efficiency for large data structures.

    Core Data Types:

    • Maps: Key-value pairs (similar to Go's map[any]any). Keys can be strings, integers, or bytes. Maps can be ordered or unordered.
    • Lists: Ordered collections (similar to Go's []any). They use 0-based indexing and support negative indexing (e.g., -1 for the last item).
  6. Understand the Aerospike Data Model

    v8

    The Aerospike data model is organized around Records, which are stored within Sets inside a Namespace.

    • Bins: Analogous to fields in a relational database. A record contains multiple bins, each with a name and a value. Supported values include integers (u/int/8, 16, 32, 64), strings, Arrays, and Maps. Complex values like Maps and Arrays can be nested.
    • Keys: Every record is uniquely addressable via a Key, which consists of a Namespace, a Set name, and the Key value itself.
    • BinMap: A type alias for map[string]any used to declare and manipulate bin data easily.
  7. Understanding OpResults return behavior

    v8

    When using Operate(), the way results are returned in record.Bins depends on how many operations target the same bin:

    1. Single operation on a bin: The result is returned directly as its underlying type (e.g., string, int, []any). It is not wrapped in OpResults.
    2. Multiple operations on the same bin: The results are wrapped in an as.OpResults type, which is a slice ([]interface{}). The elements in the slice correspond to the operations in the order they were provided.
    3. Operations on different bins: Each bin returns its value directly, regardless of how many total operations were performed in the call.

    Note: If a single operation returns a slice (like MapGetByValueOp with KEY return type), it is stored directly as []any, not wrapped in OpResults.

    // Example: Multiple Operations on the SAME bin (Returns OpResults)
    record, err := client.Operate(nil, key,
        as.MapGetByKeyOp("profile", "name", as.MapReturnType.VALUE),
        as.MapGetByKeyOp("profile", "age", as.MapReturnType.VALUE),
    )
    
    results := record.Bins["profile"].(as.OpResults) // Wrapped in OpResults
    name := results[0].(string)                     // First operation
    age := results[1].(int)                       // Second operation
    
    // Example: Single Operation on a bin (Returns Value Directly)
    record, err := client.Operate(nil, key,
        as.MapGetByKeyOp("profile", "name", as.MapReturnType.VALUE),
    )
    
    name := record.Bins["profile"].(string) // Direct value, NOT OpResults
    
    // Example: Operations on DIFFERENT bins (Each Returns Directly)
    record, err := client.Operate(nil, key,
        as.MapGetByKeyOp("profile", "name", as.MapReturnType.VALUE),
        as.MapGetByKeyOp("orders", "count", as.MapReturnType.VALUE),
    )
    
    name := record.Bins["profile"].(string) // Direct value
    count := record.Bins["orders"].(int)    // Direct value
  8. Retrieve values by Rank or Rank Range

    v8

    In ordered maps, you can retrieve items based on their sorted position (rank) relative to their values.

    Understanding Ranks:

    • Rank 0: The smallest/lowest value.
    • Rank 1: The second smallest value.
    • Rank -1: The largest/highest value (most common for leaderboards).
    • Rank -2: The second largest value.

    Key Rank Operations

    • MapGetByRankOp: Retrieves a single item at a specific rank.
    • MapGetByRankRangeOp: Retrieves items within a rank range.
    • MapGetByRankRangeCountOp: A more intuitive way to get the "Top N" items. You specify a starting rank and a count. For example, starting at rank -1 with a count of 10 retrieves the 10 highest values.
    // Get item with highest value (rank -1)
    record, err := client.Operate(nil, key,
        as.MapGetByRankOp("scores", -1, as.MapReturnType.KEY_VALUE),
    )
    
    // Get top 10 players using count (starting from highest rank -1)
    record, err := client.Operate(nil, key,
        as.MapGetByRankRangeCountOp("leaderboard", -1, 10, as.MapReturnType.KEY_VALUE),
    )
    results := record.Bins["leaderboard"].(as.OpResults)
    topPlayers := results[0].([]as.MapPair)
  9. Work with Records and Bins

    v8

    A Record is represented as a struct in the Go client. When you perform a Get operation, you receive a Record containing:

    • Bins: A BinMap (map[string]any) containing the data.
    • Key: The associated Key pointer.
    • Node: The database node that provided the record.
    • Expiration: Time-to-live in seconds.
    • Generation: The number of times the record has been updated.

    To update a record, you typically retrieve it using Get, modify the values within the Bins map, and then write it back using Put.

    // define a client to connect to
    client, err := NewClient("127.0.0.1", 3000)
    panicOnError(err)
    
    key, err := NewKey("test", "demo", "key")
    panicOnError(err)
    
    // define some bins
    bins := BinMap{
      "bin1": 42,
      "bin2": "An elephant is a mouse with an operating system",
      "bin3": []any{"Go", 2009},
    }
    
    // write the bins
    writePolicy := NewWritePolicy(0, 0)
    err = client.Put(writePolicy, key, bins)
    panicOnError(err)
    
    // read it back!
    readPolicy := NewPolicy()
    rec, err := client.Get(readPolicy, key)
    panicOnError(err)
    
    // change data
    v := rec.Bins["bin1"].(int)
    v += 1
    rec.Bins["bin1"] = v
    
    // update
    err = client.Put(nil, key, rec.Bins)
  10. Configure load generation and bin data types

    v8

    The benchmark tool allows you to control how load is generated and what kind of data is used in the bins:

    • Key Range: Use the -k switch to specify the key range for load generation.
    • Random Bin Data: Use the -R switch to generate random bin data instead of the default static data.
    • Bin Data Type: Use the -o switch to specify the type of bin data. The default is 64-bit integer values.
    • Workload Mix: Use the -w switch to define the ratio of reads to writes (e.g., RU,50 for 50% reads and 50% writes).
    • Timeouts: Use the -T switch to set a timeout for operations (in milliseconds).