NetLife Guru

Open source Go packages for fast, maintainable web systems. Built with a documentation-first approach.

Product
OverviewGolang packagesNews
Documentation
DocumentationGo LoggerGo RouterGo DB Form
Company
OverviewContactNewsGitHub
Community / Support
Supportinfo@netlife.guru
© 2026 NetLife Guru. All rights reserved.
GitHubinfo@netlife.guru
NetLife GuruNetLife GuruNetLife Guru
NetLife GuruNetLife GuruNetLife Guru
OverviewDocumentationNewsSupportContact

Golang packages

AboutMulti-DriverSQL Files
SQL TransactionsLow-Level TransactionsScylla Lightweight TransactionsScylla Batches
DBTransactions & Writes

Scylla Batches

Use Scylla batches for grouped CQL writes through the Scylla driver.

Scylla batches are used to group multiple CQL statements into a single batch request.

They are supported by the Scylla driver and are specific to Scylla/CQL workloads.

Batches are different from SQL transactions.

They do not behave like MySQL or PostgreSQL transactions with BEGIN, COMMIT, and ROLLBACK. They also do not replace lightweight transactions.

Use batches when your Scylla data model requires grouped writes.

Basic Idea

The Scylla driver exposes batch helpers through the connection.

Common batch types include:

  • logged batch
  • unlogged batch
  • counter batch

A typical flow is:

  1. create a batch
  2. add CQL statements
  3. execute the batch
batch := conn.NewLoggedBatch(ctx)

err := batch.AddSQL(`
	INSERT INTO users_by_id (id, email, name, active, created_at)
	VALUES (?, ?, ?, ?, ?)
`, id, email, name, active, createdAt)

if err != nil {
	return err
}

err = batch.Execute()
if err != nil {
	return err
}

Batch Types

Batch typePurpose
Logged batchGroup writes that should be coordinated by Scylla
Unlogged batchGroup writes without logged batch coordination
Counter batchGroup counter updates

The exact behavior depends on Scylla and CQL semantics.

Use batches carefully and only when they match your data model.

Complete Example

This example writes the same user into two query tables.

package main

import (
	"context"
	"time"

	"github.com/gocql/gocql"
	"github.com/netlifeguru/db"
	"github.com/netlifeguru/db-scylla"
)

const insertUserByIDQuery = `
	INSERT INTO users_by_id (id, email, name, active, created_at)
	VALUES (?, ?, ?, ?, ?)
`

const insertUserByEmailQuery = `
	INSERT INTO users_by_email (email, id, name, active, created_at)
	VALUES (?, ?, ?, ?, ?)
`

func InsertUserWithBatch(
	ctx context.Context,
	conn scylla.BatchConn,
	name string,
	email string,
	active bool,
) (string, error) {
	id := gocql.TimeUUID()
	createdAt := time.Now().UTC()

	batch := conn.NewLoggedBatch(ctx)

	if err := batch.AddSQL(insertUserByIDQuery, id, email, name, active, createdAt); err != nil {
		return "", err
	}

	if err := batch.AddSQL(insertUserByEmailQuery, email, id, name, active, createdAt); err != nil {
		return "", err
	}

	if err := batch.Execute(); err != nil {
		return "", err
	}

	return id.String(), nil
}

Using db.Query

You can also add a prepared db.Query to a batch.

q, err := db.Raw(insertUserByIDQuery, id, email, name, active, createdAt)
if err != nil {
	return err
}

batch := conn.NewLoggedBatch(ctx)

if err := batch.Add(q); err != nil {
	return err
}

if err := batch.Execute(); err != nil {
	return err
}

This is useful when your queries are selected or prepared before the batch is created.

Low-Level Batch Example

The low-level pattern uses db.Raw and batch.Add.

package main

import (
	"context"
	"time"

	"github.com/gocql/gocql"
	"github.com/netlifeguru/db"
	"github.com/netlifeguru/db-scylla"
)

func InsertUserWithLowLevelBatch(
	ctx context.Context,
	conn scylla.BatchConn,
	name string,
	email string,
	active bool,
) (string, error) {
	id := gocql.TimeUUID()
	createdAt := time.Now().UTC()

	q1, err := db.Raw(insertUserByIDQuery, id, email, name, active, createdAt)
	if err != nil {
		return "", err
	}

	q2, err := db.Raw(insertUserByEmailQuery, email, id, name, active, createdAt)
	if err != nil {
		return "", err
	}

	batch := conn.NewLoggedBatch(ctx)

	if err := batch.Add(q1); err != nil {
		return "", err
	}

	if err := batch.Add(q2); err != nil {
		return "", err
	}

	if err := batch.Execute(); err != nil {
		return "", err
	}

	return id.String(), nil
}

Accessing Batch Methods

The shared db.Conn interface is intentionally database-agnostic.

Scylla batch methods are Scylla-specific, so use the Scylla batch interface when you need them.

batchConn, ok := conn.(scylla.BatchConn)
if !ok {
	return errors.New("connection does not support Scylla batches")
}

Then create a batch:

batch := batchConn.NewLoggedBatch(ctx)

This keeps the shared db.Conn interface clean while still allowing Scylla-specific behavior when needed.

Logged Batch

Use NewLoggedBatch when you need Scylla logged batch behavior.

batch := conn.NewLoggedBatch(ctx)

Logged batches coordinate batch writes through Scylla’s batch log.

Use them only when the data model requires that behavior.

Unlogged Batch

Use NewUnloggedBatch for unlogged batch writes.

batch := conn.NewUnloggedBatch(ctx)

Unlogged batches avoid the batch log overhead, but they do not provide the same coordination behavior as logged batches.

Counter Batch

Use NewCounterBatch for counter updates.

batch := conn.NewCounterBatch(ctx)

Counter batches are intended for CQL counter operations.

Batches vs Lightweight Transactions

Batches and lightweight transactions solve different problems.

FeatureBatchLightweight transaction
PurposeGroup multiple statementsApply one conditional statement
Condition checknoyes
Uses [applied]noyes
Common CQLBEGIN BATCH ... APPLY BATCH behavior through driverIF NOT EXISTS, IF column = value
Best forgrouped writescompare-and-set style writes

Use lightweight transactions when you need a conditional write.

Use batches when you need grouped CQL statements.

Batches vs SQL Transactions

Scylla batches are not SQL transactions.

They do not provide the same behavior as:

BEGIN;
COMMIT;
ROLLBACK;

If you come from MySQL or PostgreSQL, do not treat Scylla batches as a direct transaction replacement.

Design your Scylla schema and write path according to Scylla data modeling rules.

When to Use Batches

Use batches when:

  • your Scylla data model requires grouped writes
  • you write the same logical entity into multiple query tables
  • you need logged, unlogged, or counter batch behavior
  • you understand the performance and modeling implications

When Not to Use Batches

Avoid batches when:

  • you are trying to make Scylla behave like a relational database
  • the statements are unrelated
  • a normal single-table write is enough
  • you want a replacement for SQL transactions
  • you are using batches for every write without a modeling reason

Related Examples

Standalone examples are available in the examples repository:

  • Scylla batch
  • Scylla low-level batch

Scylla Lightweight Transactions

Use Scylla lightweight transactions for conditional writes with IF NOT EXISTS and applied result checks.

Project Information

Documentation links, versioning policy, contribution guidelines, licensing, and project resources for the NLG DB package.

On this page

Basic IdeaBatch TypesComplete ExampleUsing db.QueryLow-Level Batch ExampleAccessing Batch MethodsLogged BatchUnlogged BatchCounter BatchBatches vs Lightweight TransactionsBatches vs SQL TransactionsWhen to Use BatchesWhen Not to Use BatchesRelated Examples