xk6_couchbase

package module
v0.0.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

README

CouchBase k6 extension

K6 extension to perform tests on couchbase.

Currently Supported Commands

  • Supports inserting a document.
  • Supports Batch insertion.
  • Supports findOne (Fetch by primary key)
  • Supports findMany (Batch fetch by primary keys)
  • Supports deleting a document by id
  • Supports upserts
  • Testing query performance
  • Prepared statement query performance

Build Instructions

For Development
git clone git@github.com:thotasrinath/xk6-couchbase.git
cd xk6-couchbase
make build

This produces a ./xk6-couchbase binary from the local source. No global xk6 is required — the Makefile invokes it via go run. See the Development section for the full set of make targets (tests, lint, example validation).

For Use

Install xk6, then build a k6 binary with the published extension:

go install go.k6.io/xk6/cmd/xk6@latest
xk6 build --with github.com/thotasrinath/xk6-couchbase@latest

Development

A Makefile wraps the common tasks (run make with no target to list them):

make build      # build a k6 binary with this extension from local source
make test       # run unit tests
make check      # gofmt + go vet + test (pre-commit gate)
make lint       # golangci-lint (see https://golangci-lint.run to install)
make validate   # build + ensure local Couchbase + run every example end-to-end

Unit tests cover the module lifecycle, the shared-connection singleton, and the per-VU connection behavior. They use k6's modulestest runtime and do not require a live Couchbase cluster (gocb.Connect is lazy), so make test runs anywhere.

make validate runs the example scripts against a local Couchbase (starting a Docker container if one isn't already reachable). See examples/README.md for details.

Examples

Document Insertion Test
import xk6_couchbase from 'k6/x/couchbase';


const client = xk6_couchbase.newClient('localhost', '<username>', '<password>');
export default () => {

    let doc = {
        correlationId: `test--couchbase`,
        title: 'Perf test experiment',
        url: 'example.com',
        locale: 'en',
        time: `${new Date(Date.now()).toISOString()}`
    };
    client.insert("test", "_default", "_default", makeId(15), doc);
}

function makeId(length) {
    let result = '';
    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    const charactersLength = characters.length;
    let counter = 0;
    while (counter < length) {
        result += characters.charAt(Math.floor(Math.random() * charactersLength));
        counter += 1;
    }
    return result;
}

Below is the example commands to test the script
# Update examples/test-insert.js with cluster credentials and bucket name
 ./xk6-couchbase run --vus 10 --duration 30s examples/test-insert.js
Batch Insert Documents
import xk6_couchbase from 'k6/x/couchbase';


const client = xk6_couchbase.newClient('localhost', '<username>', '<password>');

const batchsize = 50;

export default () => {

    var docobjs = {}

    for (var i = 0; i < batchsize; i++) {
        docobjs[makeId(15)] = getRecord();
    }

    client.insertBatch("test", "_default", "_default", docobjs);
}

function getRecord() {
    return {
        correlationId: `test--couchbase`,
        title: 'Perf test experiment',
        url: 'example.com',
        locale: 'en',
        time: `${new Date(Date.now()).toISOString()}`
    };


}

function makeId(length) {
    let result = '';
    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    const charactersLength = characters.length;
    let counter = 0;
    while (counter < length) {
        result += characters.charAt(Math.floor(Math.random() * charactersLength));
        counter += 1;
    }
    return result;
}

Exists test
import xk6_couchbase from 'k6/x/couchbase';


const client = xk6_couchbase.newClient('localhost', '<username>', '<password>');
export default () => {
    // syntax :: client.exists("<db>", "<scope>", "<keyspace>", "<docId>");
    var res = client.exists("test", "_default", "_default", "002wPJwiJArcUpz");
    console.log(res);
}

FindOne test
import xk6_couchbase from 'k6/x/couchbase';


const client = xk6_couchbase.newClient('localhost', '<username>', '<password>');
export default () => {
    // syntax :: client.findOne("<db>", "<scope>", "<keyspace>", "<docId>");
    var res = client.findOne("test", "_default", "_default", "002wPJwiJArcUpz");
    console.log(res);
}

FindMany (Batch Get) test
import xk6_couchbase from 'k6/x/couchbase';


const client = xk6_couchbase.newClient('localhost', '<username>', '<password>');
export default () => {
    // syntax :: client.findMany("<db>", "<scope>", "<keyspace>", ["<docId1>", "<docId2>", ...]);
    // Returns a map of docId -> document for keys that exist. Missing keys are omitted.
    var res = client.findMany("test", "_default", "_default", ["key1", "key2", "key3"]);
    console.log(JSON.stringify(res));
}

Document Deletion Test
import xk6_couchbase from 'k6/x/couchbase';


const client = xk6_couchbase.newClient('localhost', '<username>', '<password>');
export default () => {

    client.remove("test", "_default", "_default", "002wPJwiJArcUpz", doc);
}

Query test
import xk6_couchbase from 'k6/x/couchbase';


const client = xk6_couchbase.newClient('localhost', '<username>', '<password>');
export default () => {
    var res = client.find("select * from test._default._default  use keys \"00096zszpZaT47X\"");

    //console.log(res);

}
Query using a prepared statement
import xk6_couchbase from 'k6/x/couchbase';


const client = xk6_couchbase.newClient('localhost', '<username>', '<password>');
export default () => {
    var res = client.findByPreparedStmt("select * from test._default._default  use keys \"00096zszpZaT47X\"");

    //console.log(res);

}
Upsert test
Initial data load is expected. Data with sequential Id's could be generated with below script. File - examples/load-sequential-data.js
import xk6_couchbase from 'k6/x/couchbase';
const jobConfig = {
    vus: 10,
    count: 10000
}
export const options = {
    scenarios: {
        contacts: {
            executor: 'per-vu-iterations',
            vus: jobConfig.vus,
            iterations: jobConfig.count
        },
    },
};
const client = xk6_couchbase.newClient('localhost', '<username>', '<password>');
export default function () {
    let doc = {
        correlationId: `test--couchbase`,
        title: 'Perf test experiment',
        url: 'example.com',
        locale: 'en',
        time: `${new Date(Date.now()).toISOString()}`
    };

    let docId = ((__VU - 1) * jobConfig.count) + __ITER;
    client.insert("test", "_default", "_default", String(docId), doc);
}
Upsert test script to generate random id in range and excute Upsert. File - examples/test-upsert.js
import xk6_couchbase from 'k6/x/couchbase';
import {randomIntBetween} from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';

const client = xk6_couchbase.newClient('localhost', '<username>', '<password>');
export default () => {

    let doc = {
        correlationId: `test--couchbase`,
        title: 'Perf test experiment',
        url: 'example.com',
        locale: 'en',
        time: `${new Date(Date.now()).toISOString()}`,
        randomNo: randomIntBetween(0,1000000)
    };
    client.upsert("test", "_default", "_default", String(randomIntBetween(0, 100000)), doc);
}
Client with options

Default NewClient is a good start to get familiar with the extension with little wiring and get going. For refined flexibility of connection management during the tests, following additional interfaces are provided:

  • NewClientWithSharedConnection: Shares a single connection across all the VUs and maximizes the QPS without affecting client resources.
  • NewClientPerVU: Creates new client connection for each VU. Useful for testing max number of connections that the cluster can handle.

In addition to controlling number of connections, both the functions provide:

  • Ability to pre-warm the buckets as part of initialization.
  • Ability to set timeout duration for bucket readiness (WaitUntilReady timeout).
  • Ability to control the connection buffer size. By default couchbase client uses 20MB buffer size which limits the number of concurrent connections that can be created by a single client instance.
Example for wiring client with shared connection across VUs
import xk6_couchbase from 'k6/x/couchbase';


const dbConfig = { hostname: 'localhost', username: '<username>', password: '<password>' };
const bucketsToPreWarm = ['test'];
const client = xk6_couchbase.newClientWithSharedConnection(dbConfig, bucketsToPreWarm, "5s");
export default () => {
    // syntax :: client.findOne("<db>", "<scope>", "<keyspace>", "<docId>");
    var res = client.findOne("test", "_default", "_default", "002wPJwiJArcUpz");
    console.log(res);
}

Example for wiring client with unique connections per VUs
import xk6_couchbase from 'k6/x/couchbase';


const dbConfig = { hostname: 'localhost', username: '<username>', password: '<password>' };
const bucketsToPreWarm = ['test'];
const client = xk6_couchbase.newClientPerVU(dbConfig, bucketsToPreWarm, "5s");
export default () => {
    // syntax :: client.findOne("<db>", "<scope>", "<keyspace>", "<docId>");
    var res = client.findOne("test", "_default", "_default", "002wPJwiJArcUpz");
    console.log(res);
}

Documentation

Overview

Package xk6_couchbase implements a k6 extension (k6/x/couchbase) for running load tests against Couchbase clusters.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a connected Couchbase client exposed to JS test scripts. It is returned by the NewClient* constructors and caches per-bucket connections.

func (*Client) Close added in v0.0.9

func (c *Client) Close() error

Close closes the underlying cluster connection.

func (*Client) Exists added in v0.0.9

func (c *Client) Exists(bucketName string, scope string, collection string, docID string) error

Exists reports whether a document with the given ID exists.

func (*Client) Find

func (c *Client) Find(query string) (any, error)

Find executes a N1QL query and returns the last row read.

func (*Client) FindByPreparedStmt

func (c *Client) FindByPreparedStmt(query string, params ...interface{}) (any, error)

FindByPreparedStmt executes a N1QL query with positional parameters and returns the last row read.

func (*Client) FindMany added in v0.0.10

func (c *Client) FindMany(bucketName string, scope string, collection string, keys []string) (map[string]interface{}, error)

FindMany fetches multiple documents by key in a single bulk operation, returning a map of document ID to document. Keys that do not exist (or fail to decode) are omitted from the result.

func (*Client) FindOne

func (c *Client) FindOne(bucketName string, scope string, collection string, docID string) (any, error)

FindOne fetches a single document by ID.

func (*Client) Insert

func (c *Client) Insert(bucketName string, scope string, collection string, docID string, doc any) error

Insert adds a new document, failing if the document ID already exists.

func (*Client) InsertBatch

func (c *Client) InsertBatch(bucketName string, scope string, collection string, docs map[string]any) error

InsertBatch inserts multiple documents (keyed by document ID) in a single bulk operation.

func (*Client) Remove

func (c *Client) Remove(bucketName string, scope string, collection string, docID string) error

Remove deletes a document by ID using majority durability.

func (*Client) Upsert added in v0.0.6

func (c *Client) Upsert(bucketName string, scope string, collection string, docID string, doc any) error

Upsert inserts a document, replacing it if the document ID already exists.

type CouchBase

type CouchBase struct {
	// contains filtered or unexported fields
}

CouchBase is the JS-facing API object. Shared state lives on root.

func (*CouchBase) NewClient

func (c *CouchBase) NewClient(connectionString string, username string, password string) interface{}

NewClient returns a client using the default options (shared connection). On success it returns the *Client; on failure it returns an error value to the JS runtime.

func (*CouchBase) NewClientPerVU added in v0.0.9

func (c *CouchBase) NewClientPerVU(dbConfig DBConfig, bucketsToWarm []string, bucketReadinessDuration string, connectionBufferSizeBytes int) (*Client, error)

NewClientPerVU returns a client that opens a dedicated cluster connection for each VU. Useful for exercising the maximum number of connections a cluster can handle.

func (*CouchBase) NewClientWithOptions added in v0.0.9

func (c *CouchBase) NewClientWithOptions(dbConfig DBConfig, opts options) (*Client, error)

NewClientWithOptions returns a client configured with the given options, optionally pre-warming the requested buckets.

func (*CouchBase) NewClientWithSharedConnection added in v0.0.9

func (c *CouchBase) NewClientWithSharedConnection(dbConfig DBConfig, bucketsToWarm []string, bucketReadinessDuration string, connectionBufferSizeBytes int) (*Client, error)

NewClientWithSharedConnection returns a client backed by a single cluster connection shared across all VUs, maximizing QPS without exhausting client resources.

type DBConfig added in v0.0.9

type DBConfig struct {
	Hostname string `json:"connection_string,omitempty"`
	Username string `json:"-"`
	Password string `json:"-"`
}

DBConfig holds the connection details for a Couchbase cluster.

type ModuleInstance added in v0.0.10

type ModuleInstance struct {
	// contains filtered or unexported fields
}

ModuleInstance is created once per VU by k6. Each instance points back to the single RootModule so VUs can share the singleton connection when requested.

func (*ModuleInstance) Exports added in v0.0.10

func (mi *ModuleInstance) Exports() k6modules.Exports

Exports exposes the CouchBase API object to the JS runtime as the default export, preserving the `import xk6_couchbase from 'k6/x/couchbase'` usage.

type RootModule added in v0.0.10

type RootModule struct {
	// contains filtered or unexported fields
}

RootModule is created once per k6 run. It holds state that is shared across all VUs, namely the lazily-initialized singleton client used in shared connection mode.

func New added in v0.0.10

func New() *RootModule

New builds the root module. It is registered once in init.

func (*RootModule) NewModuleInstance added in v0.0.10

func (r *RootModule) NewModuleInstance(vu k6modules.VU) k6modules.Instance

NewModuleInstance is called by k6 for every VU, handing it the shared root.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL