News:

SMF - Just Installed!

Main Menu

Speed Up MongoDB Queries with Redis on Golang

Started by Administrator, Dec 31, 2022, 05:50 AM

Previous topic - Next topic

Administrator

Introduction

A cache utilizes the RAM of your computer to store and serve frequently accessed data from backend systems like databases. As a result, a caching layer improves performance, minimizes costs, and reduces the load on a database.

The Redis in-memory database server is a suitable caching application because it requires minimal setup and scales well in a production environment. When you implement a cache, your application first queries Redis to get data. If the cache is empty, the application fetches data from the database. Then, it creates a cache for future queries. This in-memory caching solution is several times faster when compared to rotating or modern Solid State Disks (SSD).

This guide walks you through the process of implementing a caching layer with Redis to speed up MongoDB queries using the Go programming language.

Prerequisites
Deploy an Ubuntu 20.04 server.
Create a non-root sudo user.
Install and configure a MongoDB server.
Set up a Redis server.
Download and install the Go programming language.

1. Set Up a Sample Database
This guide uses a sample MongoDB database to store data permanently on a disk. Follow the steps below to create the database.

Log in to your MongoDB server. Replace mongo_db_admin with the correct user account.

$ mongosh -u mongo_db_admin -p --authenticationDatabase admin
Enter your password to proceed.

Run the statement below to create an e-commerce database.

test> use e-commerce
Output.

switched to db e-commerce
Insert five documents into a new products collection.

e-commerce> db.products.insertMany([
            {"product_id" : 1,
             "product_name" : "INSTANT WATER HEATER",
             "retail_price" : 45.55   
            },
            {"product_id" : 2,
             "product_name" : "DOUBLE SOCKET WITH PATTRESS",
             "retail_price" : 6.65   
            },
            {"product_id" : 3,
             "product_name" : "80MM USB PRINTER",
             "retail_price" : 125.95
            },
            {"product_id" : 4,
             "product_name" : "FITNESS SMARTWATCH",
             "retail_price" : 39.85
            },
            {"product_id" : 5,
             "product_name" : "3.1A FAST CHARGER",
             "retail_price" : 23.90
            }     
           ]);
Output.

{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId("625d2694dd1d8bd34f469875"),
    '1': ObjectId("625d2694dd1d8bd34f469876"),
    '2': ObjectId("625d2694dd1d8bd34f469877"),
    '3': ObjectId("625d2694dd1d8bd34f469878"),
    '4': ObjectId("625d2694dd1d8bd34f469879")
 }
}
Querying the products collection to verify the documents.

e-commerce> db.products.find()
Output.

[
  {
    _id: ObjectId("625d2694dd1d8bd34f469875"),
    product_id: 1,
    product_name: 'INSTANT WATER HEATER',
    retail_price: 45.55
  },
  {
    _id: ObjectId("625d2694dd1d8bd34f469876"),
    product_id: 2,
    product_name: 'DOUBLE SOCKET WITH PATTRESS',
    retail_price: 6.65
  },
  {
    _id: ObjectId("625d2694dd1d8bd34f469877"),
    product_id: 3,
    product_name: '80MM USB PRINTER',
    retail_price: 125.95
  },
  {
    _id: ObjectId("625d2694dd1d8bd34f469878"),
    product_id: 4,
    product_name: 'FITNESS SMARTWATCH',
    retail_price: 39.85
  },
  {
    _id: ObjectId("625d2694dd1d8bd34f469879"),
    product_id: 5,
    product_name: '3.1A FAST CHARGER',
    retail_price: 23.9
  }
]
Log out from the MongoDB database.

e-commerce> quit
Create the main.go File
The main.go file is the entry point in this sample application. This file contains the main(...){...} function that fires when your application starts. Execute the steps below to set up the file.

Create a new directory for your project. This approach keeps your project organized and avoids conflicting your source code with the rest of the Linux files.

$ mkdir project
Switch to the new project directory.

$ cd project
Open a new main.go file in an editor.

$ nano main.go
Enter the following information into the file. Replace mongo_db_admin and EXAMPLE_PASSWORD with the correct account details for the MongoDB server.

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "net/http"
)

const ( 
    MONGO_DB_USER = "mongo_db_admin"
    MONGO_DB_PASS = "EXAMPLE_PASSWORD"
    MONGO_DB_NAME = "e-commerce"   
)

func main() {
     http.HandleFunc("/products", requestHandler)
     http.ListenAndServe(":8080", nil)
}

func requestHandler(w http.ResponseWriter, req *http.Request) {   

    w.Header().Set("Content-Type", "application/json")

    var respMessage map[string]interface{}
    var respErr error

    ctx := context.Background()

    isCached, productsCache, err := getFromCache(ctx)

    if err != nil { 

        respErr = err

    } else {

        if isCached == true {

            respMessage = productsCache
            respMessage["_source"] = "Redis Cache"

        } else {

            respMessage, err = getFromDb(ctx);

            if err != nil {       
                respErr = err
            }   

            err = addToCache(ctx, respMessage)           

            if err != nil {       
                respErr = err
            }

            respMessage["_source"] = "MongoDB database"
        }
    }

    if respErr != nil  {

        fmt.Fprintf(w, respErr.Error())

    } else {

        enc := json.NewEncoder(w)
        enc.SetIndent("", "  ")

        if err := enc.Encode(respMessage); err != nil {       
            fmt.Fprintf(w, err.Error())
        }

    }   
}
Save and close the file.

In the above file, you're creating an HTTP server that listens for incoming connections on port 8080.

In the main(...){...} function, you're redirecting incoming requests to the requestHandler(...){...} function. Under this function, you're using the isCached, productsCache, err := getFromCache(ctx) statement to retrieve products from a Redis Cache. In case the cache is empty, you're fetching the data from the MongoDB database using the statement respMessage, err = getFromDb(ctx);.

Then, you're outputting the products' data in a JSON format. You're also appending a _source attribute to mark the data source. That is, either from the cache or the database.