DEV Community

Cover image for Cassandra & GoLang
bseyhan
bseyhan

Posted on

Cassandra & GoLang

Hi there,

Did you read my latest implementation of PostgreSQL with GoLang? PostgresWithGo If you said of course :) go head... Do you know have any knowledge about Cassandra? Don't worry neither am I've not. I like to share about the subjects I work on. So be relax. Are you curious about NOSQl and GoLang? In this post I'll share my Cassandra Db and GoLang knowledge. You'll ask these questions ask yourself, What is Cassandra Db then how to install Cassandra DB to your local machine, which package should I use for it. All of these are in here.

Step 1 : What is Cassandra DB
Step 2 : How to install it
Step 3 : Important commands
Step 4 : Connect to database
Step 5 : Implementation
Step 6 : Postman Tests

Let's go to work and I hope you'll enjoy it.

Step 1: What is Cassandra DB

NoSQL databases are called “Not Only SQL” or “Non-relational” databases. NoSQL databases store and retrieve data other than tabular relations such as relation databases.

Image description

Image description

Elastic scalability − Cassandra is highly scalable; it allows to add more hardware to accommodate more customers and more data as per requirement.

Always on architecture − Cassandra has no single point of failure and it is continuously available for business-critical applications that cannot afford a failure.

Fast linear-scale performance − Cassandra is linearly scalable, i.e., it increases your throughput as you increase the number of nodes in the cluster. Therefore it maintains a quick response time.

Flexible data storage − Cassandra accommodates all possible data formats including: structured, semi-structured, and unstructured. It can dynamically accommodate changes to your data structures according to your need.

Easy data distribution − Cassandra provides the flexibility to distribute data where you need by replicating data across multiple data centers.

Transaction support − Cassandra supports properties like Atomicity, Consistency, Isolation, and Durability (ACID).

Fast writes − Cassandra was designed to run on cheap commodity hardware. It performs blazingly fast writes and can store hundreds of terabytes of data, without sacrificing the read efficiency.

Data Types

Image description

Step 2: How to install

I'm using mac os. I've already installed Docker application on my machine. So may be you want to install only docker container. Install Docker

docker pull cassandra
docker run -d --name cassandra -p 9042:9042 cassandra

Image description

In this repository I created docker-compose.yml file that file contains with required images.


  cassandra:
    image: cassandra:latest
    container_name: cassandra
    ports: 
      - 7000:7000
      - 7001:7001
      - 7199:7199
      - 9042:9042
      - 9160:9160
    restart: always
    environment:
       - CASSANDRA_BROADCAST_ADDRESS=host.docker.internal
       - CASSANDRA_SEEDS=host.docker.internal 
    volumes:
        - ./out/cassandra_data:/var/lib/cassandra
    healthcheck:
        test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"]
        interval: 15s
        timeout: 10s
        retries: 10

Enter fullscreen mode Exit fullscreen mode
  • 7199 - JMX (was 8080 pre Cassandra 0.8.xx)
  • 7000 - Internode communication (not used if TLS enabled)
  • 7001 - TLS Internode communication (used if TLS enabled)
  • 9160 - Thrift client API
  • 9042 - CQL native transport port

Step 3: Important Commands

After the installation since I want to create a database, I have to run this command. What is cqlsh? It is the interface. cqlsh is a command-line interface for interacting with Cassandra using CQL (the Cassandra Query Language). Documentation

docker exec -it cassandra cqlsh

Image description

Let's create a todo table.

Image description

Step 4: Connect to database

Install the VSCode extension for Cassandra.

Image description

Image description

Step 5 : Implementation

Project structure:

Image description

--> cmd
--> api(main.go) this file deal with read config.yml file
--> util(config.go & errors.go) config.go is related with application configurations, errors.go is a custom response implementation.

--> pkg
--> api (api.go) this file integrated to initialize


func Initialize(config utils.Configuration) {

    // Creates a gin router with default middleware:
    router := gin.Default()

    fmt.Printf("%+v\n", config)

    //register database ,repositories and handlers
    session := cassandra.ConnectDatabase(config.Database.Url, config.Database.Keyspace)

    repository := db.NewTodoRepository(session)
    orderHandler := handler.NewTodoHandler(&repository)

    router.GET("/ping", orderHandler.HealthCheck)

    router.POST("/", orderHandler.CreateTodo)
    router.GET("api/v1/todo/:id", orderHandler.GetTodoById)

    //run the server :8080
    router.Run(":8080")
}
Enter fullscreen mode Exit fullscreen mode

--> client/cassandra

The ConnectDatabase function take 2 parameters from api.go file. After creation that function response session. This session

func ConnectDatabase(url string, keyspace string) *gocql.Session {

    cluster := gocql.NewCluster(url)
    cluster.Keyspace = keyspace
    cluster.Consistency = gocql.Quorum

    session, _ := cluster.CreateSession()

    return session
}
Enter fullscreen mode Exit fullscreen mode

Click fn+12 CreateSession

// CreateSession initializes the cluster based on this config and returns a
// session object that can be used to interact with the database.
func (cfg *ClusterConfig) CreateSession() (*Session, error) {
    return NewSession(*cfg)
}
Enter fullscreen mode Exit fullscreen mode

-->handler
TodoHandler related with request database and response JSON data also error handling is done for that level. All function take c *gin.Context parameter also these functions has receiver function (t *todoHandler) which means that you can access the repository with t.Save or t.GetById.

func (t *todoHandler) CreateTodo(c *gin.Context) {
    var todo model.Todo

    c.BindJSON(&todo)

    todo.Id = uuid.New().String()
    log.Info("Data is ", todo)
    data, err := t.repo.Save(todo)

    if err != nil {
        c.JSON(http.StatusBadRequest, utils.BadRequestError("insert operation failed!", err))
    }

    c.JSON(http.StatusCreated, gin.H{"todo": data})
}

func (t *todoHandler) GetTodoById(c *gin.Context) {

    id := c.Param("id")

    todo, err := t.repo.GetById(id)
    if err != nil {
        c.JSON(http.StatusBadRequest, utils.BadRequestError("todo not found", err))
    }

    c.JSON(http.StatusOK, gin.H{"todo": todo})
}

Enter fullscreen mode Exit fullscreen mode

GIN : c.JSON(http.StatusOK, gin.H{"todo": todo})
Echo: return c.JSON(http.StatusOK, data)

// JSON serializes the given struct as JSON into the response body.
// It also sets the Content-Type as "application/json".
func (c *Context) JSON(code int, obj interface{}) {
    c.Render(code, render.JSON{Data: obj})
}
Enter fullscreen mode Exit fullscreen mode

Binding JSON data in GIN

// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).
func (c *Context) BindJSON(obj interface{}) error {
    return c.MustBindWith(obj, binding.JSON)
}
Enter fullscreen mode Exit fullscreen mode

In repository layer has "github.com/gocql/gocql" package that's is related the queries and executions. Cassandra DB queries similar to PostgreSQL, MsSQL or MySQL queries.

func (t *todoRepository) Save(todo model.Todo) (*model.Todo, error) {

    var query string = "INSERT INTO todo(id,title,content) VALUES(?,?,?)"

    if err := t.session.Query(query, todo.Id, todo.Title, todo.Content).Exec(); err != nil {
        return nil, err
    }

    return &todo, nil
}

func (t *todoRepository) GetById(id string) (*model.Todo, error) {

    var todo model.Todo

    var query string = `SELECT id,title,content FROM todo where id=?`

    if err := t.session.Query(query, id).Scan(&todo.Id, &todo.Title, &todo.Content); err != nil {

        if err == gocql.ErrNotFound {
            return nil, err
        }

        return nil, err
    }

    return &todo, nil
}

Enter fullscreen mode Exit fullscreen mode

Now Let's run the docker-compose file. Cassandra DB has own health check configuration.
docker-compose up

    healthcheck:
        test: ["CMD", "cqlsh", "-u cassandra", "-p cassandra" ,"-e describe keyspaces"]
        interval: 15s
        timeout: 10s
        retries: 10
Enter fullscreen mode Exit fullscreen mode

When database is running you can see the status is health

Image description

Step 6: Postman Test

  • run the program Image description

Image description

Image description

References

TutorialPoint
Guru99

Github Repo

Thank you.

Discussion (1)

Collapse
clun profile image
Cédrick Lunven

Thank you for this great post !! Cassandra is hot.

An improvement idea. In the first figure when you describe Cassandra I do not think the "schemaless" statement is true. As Cassandra is using tables with strong validation we cannot really say "schemaless".

Cheers !