Cache Request Execution Results

GoGoBeginner
Practice Now

Introduction

In this project, you will learn how to implement a caching library that supports expiration time based on the singleflight package in Go. This caching library can be used in distributed systems to cache the execution results of requests, improving the performance of your application.

👀 Preview

$ /usr/local/go/bin/go test --race
PASS
ok      cacheflight     1.263s

🎯 Tasks

In this project, you will learn:

  • How to initialize a Go module and install the required packages
  • How to implement the caching library using the singleflight package
  • How to test the caching library to ensure it works as expected

🏆 Achievements

After completing this project, you will be able to:

  • Understand the basic concepts of caching and the singleflight package in Go
  • Implement a caching library that supports expiration time
  • Integrate the caching library into your distributed system to improve the performance of your application

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL go(("`Go`")) -.-> go/DataTypesandStructuresGroup(["`Data Types and Structures`"]) go(("`Go`")) -.-> go/ObjectOrientedProgrammingGroup(["`Object-Oriented Programming`"]) go(("`Go`")) -.-> go/ErrorHandlingGroup(["`Error Handling`"]) go(("`Go`")) -.-> go/ConcurrencyGroup(["`Concurrency`"]) go(("`Go`")) -.-> go/AdvancedTopicsGroup(["`Advanced Topics`"]) go/DataTypesandStructuresGroup -.-> go/maps("`Maps`") go/DataTypesandStructuresGroup -.-> go/structs("`Structs`") go/ObjectOrientedProgrammingGroup -.-> go/methods("`Methods`") go/ErrorHandlingGroup -.-> go/errors("`Errors`") go/ConcurrencyGroup -.-> go/goroutines("`Goroutines`") go/ConcurrencyGroup -.-> go/waitgroups("`Waitgroups`") go/ErrorHandlingGroup -.-> go/defer("`Defer`") go/AdvancedTopicsGroup -.-> go/time("`Time`") subgraph Lab Skills go/maps -.-> lab-301256{{"`Cache Request Execution Results`"}} go/structs -.-> lab-301256{{"`Cache Request Execution Results`"}} go/methods -.-> lab-301256{{"`Cache Request Execution Results`"}} go/errors -.-> lab-301256{{"`Cache Request Execution Results`"}} go/goroutines -.-> lab-301256{{"`Cache Request Execution Results`"}} go/waitgroups -.-> lab-301256{{"`Cache Request Execution Results`"}} go/defer -.-> lab-301256{{"`Cache Request Execution Results`"}} go/time -.-> lab-301256{{"`Cache Request Execution Results`"}} end

Initialize the Module and Install Required Packages

In this step, you will learn how to initialize the module and install the required packages for the project.

  1. Open the terminal in the /home/labex/project directory.

  2. Initialize the module using the following command:

    $ /usr/local/go/bin/go mod init cacheflight
  3. Install the required packages using the following commands:

    $ /usr/local/go/bin/go get github.com/golang/groupcache/singleflight
    $ /usr/local/go/bin/go get github.com/stretchr/testify/assert

Implement the Caching Library

In this step, you will implement the caching library based on the singleflight package.

  1. Open the cacheflight.go file in the /home/labex/project directory.

  2. Add the necessary content to import.

    import (
        "sync"
        "time"
    
        "github.com/golang/groupcache/singleflight"
    )
  3. Add the necessary content to type Group and type cacheResult.

    // cacheResult is cache result
    type cacheResult struct {
        data      interface{} // the cache data
        ctime     time.Time   // cache create time
        isRunning chan bool   // is the function running
    }
    
    // Group is core struct
    type Group struct {
        cacheExpiration time.Duration          // cache expiration
        sfg             *singleflight.Group    // singleflight group
        cache           map[string]cacheResult // cache result
        mu              sync.Mutex             // mutex
    }
  4. Implement the NewGroup function to return a new caching group with the specified cache expiration time.

    func NewGroup(cacheExpiration time.Duration) (group *Group) {
        group = &Group{
            sfg:             &singleflight.Group{},
            cache:           make(map[string]cacheResult),
            cacheExpiration: cacheExpiration,
        }
        return
    }
  5. Implement the do function to handle the caching logic.

    func (g *Group) do(key string, fn func() (interface{}, error)) (ret interface{}, err error) {
        return g.sfg.Do(key, func() (interface{}, error) {
            g.mu.Lock()
            result, ok := g.cache[key]
            if result.isRunning != nil {
                g.mu.Unlock()
                // wait for fn done
                <-result.isRunning
                return g.do(key, fn)
            } else if !ok || result.ctime.Add(g.cacheExpiration).Before(time.Now()) {
                // if the cache is not exist or expired
                var run = make(chan bool)
                result.isRunning = run
                g.cache[key] = result
                // when fn done and close run channel, let other goroutine can get the cache
                defer close(run)
            } else {
                // if the cache is exist and not expired
                g.mu.Unlock()
                return result.data, nil
            }
    
            g.mu.Unlock()
    
            // different key can run at the same time
            ret, err = fn()
            if err != nil {
                return ret, nil
            }
    
            result = cacheResult{
                data:  ret,
                ctime: time.Now(),
            }
            g.mu.Lock()
            g.cache[key] = result
            g.mu.Unlock()
    
            return ret, nil
        })
    }
  6. Implement the Do function to be a wrapper around the do function.

    func (g *Group) Do(key string, fn func() (interface{}, error)) (ret interface{}, err error) {
        return g.do(key, fn)
    }

Test the Caching Library

In this step, you will test the caching library to ensure it works as expected.

  1. Open the terminal in the /home/labex/project directory.

  2. Run the following command to execute the tests:

    $ cd /home/labex/project
    $ /usr/local/go/bin/go test --race
  3. If the cache function has been implemented correctly, you will see the following output:

    PASS
    ok      cacheflight     1.263s
  4. If you encounter a timeout error, execute the following commands in the terminal and then click the re-inspect button to pass the check:

    $ cd /home/labex/project
    $ /usr/local/go/bin/go test --race

Congratulations! You have successfully implemented the caching library based on the singleflight package. You can now use this library in your distributed system to cache the execution results of requests and improve the performance of your application.

Summary

Congratulations! You have completed this project. You can practice more labs in LabEx to improve your skills.

Other Go Tutorials you may like