Skip to content

Commit

Permalink
Merge pull request #147 from lazybytez/feature/add-redis-cache
Browse files Browse the repository at this point in the history
Add Redis as cache provider
  • Loading branch information
pascal-zarrad authored Apr 15, 2023
2 parents 11ca2a2 + 1121d7c commit f087bb9
Show file tree
Hide file tree
Showing 28 changed files with 469 additions and 218 deletions.
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
TOKEN="your-secret-token-here"
DATABASE_MODE="SQLite"
DATABASE_URL=""
DATABASE_URL="postgres://jojo:[email protected]:5432/jojo"
CACHE_MODE=memory
CACHE_DSN=""
CACHE_DSN="redis://:[email protected]:6379"
WEBAPI_MODE="debug"
WEBAPI_BIND=":8080"
WEBAPI_HOST="localhost:8080"
Expand Down
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ run: openapi/generate
build: openapi/generate
go build -ldflags "-X github.com/lazybytez/jojo-discord-bot/build.Version=edge -X github.com/lazybytez/jojo-discord-bot/build.CommitSHA=`git rev-parse --short HEAD`" .

# === Database services ===
# =====================
# Start database and redis development instances
.PHONY: services/start
services/start:
docker-compose up -d

# Stop database and redis development instances
.PHONY: services/stop
services/stop:
docker-compose stop

# Destroy database and redis development instances
.PHONY: services/destroy
services/destroy:
docker-compose down -v

# === Quality Assurance ===
# =========================
# Runs tests with specified arguments
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,25 @@ Local linting to assure code styling.
make lint
```

### Database services
To use the local PostgreSQL and Redis it is necesary to have `Docker` and `docker-compose`
installed locally.

Start local database and redis:
```bash
make services/start
```

Stop local database and redis:
```bash
make services/stop
```

Destroy local database and redis:
```bash
make services/start
```

## Contributing

If you want to take part in contribution, like fixing issues and contributing directly to the code base, please visit
Expand Down
2 changes: 1 addition & 1 deletion api/entities/audit_log_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (alcem *AuditLogConfigEntityManager) GetByGuildId(guildId uint) (*AuditLogC
return auditLogConfig, err
}

cache.Update(cacheKey, *auditLogConfig)
_ = cache.Update(cacheKey, *auditLogConfig)

return auditLogConfig, nil
}
Expand Down
6 changes: 4 additions & 2 deletions api/entities/audit_log_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ func (suite *AuditLogConfigEntityManagerTestSuite) SetupTest() {
&suite.em,
}

cache.Init(cache.ModeMemory, 10*time.Minute, "")
err := cache.Init(cache.ModeMemory, 10*time.Minute, "")
suite.NoError(err)
}

func (suite *AuditLogConfigEntityManagerTestSuite) TestNewAuditLogConfigEntityManager() {
Expand Down Expand Up @@ -98,7 +99,8 @@ func (suite *AuditLogConfigEntityManagerTestSuite) TestGetByGuildIDWithCache() {
GuildID: testGuildId,
}

cache.Update(testCacheKey, *testAuditLogConfig)
err := cache.Update(testCacheKey, *testAuditLogConfig)
suite.NoError(err)

// Do not expect call of GetFirstEntity or DB calls
// When GetFirstEntity is called, test will fail as call is unexpected
Expand Down
4 changes: 2 additions & 2 deletions api/entities/global_component_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ func (gem *GlobalComponentStatusEntityManager) Get(registeredComponentStatusId u
}

// Invalidate cache item (if present)
cache.Update(gem.getCacheKey(registeredComponentStatusId), *globalCompStatus)
_ = cache.Update(gem.getCacheKey(registeredComponentStatusId), *globalCompStatus)

return globalCompStatus, err
return globalCompStatus, nil
}

// GetDisplayString returns the string that indicates whether a component is
Expand Down
6 changes: 4 additions & 2 deletions api/entities/global_component_status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ func (suite *GlobalComponentStatusEntityManagerTestSuite) SetupTest() {
&suite.em,
}

cache.Init(cache.ModeMemory, 10*time.Minute, "")
err := cache.Init(cache.ModeMemory, 10*time.Minute, "")
suite.NoError(err)
}

func (suite *GlobalComponentStatusEntityManagerTestSuite) TestNewGlobalComponentStatusEntityManager() {
Expand Down Expand Up @@ -96,7 +97,8 @@ func (suite *GlobalComponentStatusEntityManagerTestSuite) TestGetWithCache() {
ComponentID: testId,
}

cache.Update(testCacheKey, *testGlobalComponentStatus)
err := cache.Update(testCacheKey, *testGlobalComponentStatus)
suite.NoError(err)

// Do not expect call of GetFirstEntity or DB calls
// When GetFirstEntity is called, test will fail as call is unexpected
Expand Down
4 changes: 2 additions & 2 deletions api/entities/guild.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ func (gem *GuildEntityManager) Get(guildId string) (*Guild, error) {
return &Guild{}, err
}

cache.Update(gem.getCacheKeyFromStringGuildId(guildId), *guild)
_ = cache.Update(gem.getCacheKeyFromStringGuildId(guildId), *guild)

return guild, err
return guild, nil
}

// Count returns the number of all guilds stored in the entities
Expand Down
2 changes: 1 addition & 1 deletion api/entities/guild_component_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (gcsem *GuildComponentStatusEntityManager) Get(guildId uint, componentId ui
return guildCompStatus, err
}

cache.Update(cacheKey, *guildCompStatus)
_ = cache.Update(cacheKey, *guildCompStatus)

return guildCompStatus, nil
}
Expand Down
6 changes: 4 additions & 2 deletions api/entities/guild_component_status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ func (suite *GuildComponentStatusEntityManagerTestSuite) SetupTest() {
&suite.em,
}

cache.Init(cache.ModeMemory, 10*time.Minute, "")
err := cache.Init(cache.ModeMemory, 10*time.Minute, "")
suite.NoError(err)
}

func (suite *GuildComponentStatusEntityManagerTestSuite) TestGetCacheKey() {
Expand Down Expand Up @@ -115,7 +116,8 @@ func (suite *GuildComponentStatusEntityManagerTestSuite) TestGetWithCache() {
ComponentID: componentId,
}

cache.Update(testCacheKey, *testGuildComponentStatus)
err := cache.Update(testCacheKey, *testGuildComponentStatus)
suite.NoError(err)

// Do not expect call of GetFirstEntity or DB calls
// When GetFirstEntity is called, test will fail as call is unexpected
Expand Down
6 changes: 4 additions & 2 deletions api/entities/guild_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ func (suite *GuildEntityManagerTestSuite) SetupTest() {
&suite.em,
}

cache.Init(cache.ModeMemory, 10*time.Minute, "")
err := cache.Init(cache.ModeMemory, 10*time.Minute, "")
suite.NoError(err)
}

func (suite *GuildEntityManagerTestSuite) TestNewGuildEntityManager() {
Expand Down Expand Up @@ -99,7 +100,8 @@ func (suite *GuildEntityManagerTestSuite) TestGetWithCache() {
GuildID: testId,
}

cache.Update(testCacheKey, *testGuild)
err := cache.Update(testCacheKey, *testGuild)
suite.NoError(err)

// Do not expect call of GetFirstEntity or DB calls
// When GetFirstEntity is called, test will fail as call is unexpected
Expand Down
4 changes: 2 additions & 2 deletions api/entities/registered_component.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ func (rgem *RegisteredComponentEntityManager) Get(registeredComponentCode Compon
return regComp, err
}

cache.Update(cacheKey, *regComp)
_ = cache.Update(cacheKey, *regComp)

return regComp, err
return regComp, nil
}

// GetAvailable returns all components that have been registered
Expand Down
6 changes: 4 additions & 2 deletions api/entities/registered_component_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ func (suite *RegisteredComponentEntityManagerTestSuite) SetupTest() {
[]ComponentCode{},
}

cache.Init(cache.ModeMemory, 10*time.Minute, "")
err := cache.Init(cache.ModeMemory, 10*time.Minute, "")
suite.NoError(err)
}

func (suite *RegisteredComponentEntityManagerTestSuite) TestNewRegisteredComponentEntityManager() {
Expand Down Expand Up @@ -99,7 +100,8 @@ func (suite *RegisteredComponentEntityManagerTestSuite) TestGetWithCache() {
Code: testCode,
}

cache.Update(testCacheKey, *testRegisteredComponent)
err := cache.Update(testCacheKey, *testRegisteredComponent)
suite.NoError(err)

// Do not expect call of GetFirstEntity or DB calls
// When GetFirstEntity is called, test will fail as call is unexpected
Expand Down
11 changes: 5 additions & 6 deletions api/slash_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,14 +295,13 @@ func (suite *SlashCommandManagerTestSuite) TestGetCommandsForComponentWithComman
}

result := suite.slashCommandManager.GetCommandsForComponent(testComponentCode)
expected := []*Command{
foundCommandOne,
foundCommandTwo,
}

suite.Len(result, 2)

suite.Equal(foundCommandOne.c.Code, result[0].c.Code)
suite.Equal(foundCommandOne.Category, result[0].Category)

suite.Equal(foundCommandTwo.c.Code, result[1].c.Code)
suite.Equal(foundCommandTwo.Category, result[1].Category)
suite.Equal(expected, result)
}

func TestSlashCommandManager(t *testing.T) {
Expand Down
7 changes: 6 additions & 1 deletion core_components/bot_core/command/module/rate_limiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ func increaseRateLimitCount(guild *entities.Guild) bool {
}

toggleCount += 1
cache.Update(cacheKey, toggleCount)
err := cache.Update(cacheKey, toggleCount)
if nil != err {
C.Logger().Err(err, fmt.Sprintf(
"Failed to store incremented module toggle rate limit count in cache for guild %d",
guild.GuildID))
}

return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@ func HandleSyncCommandSubCommand(
C.SlashCommandManager().SyncApplicationComponentCommands(s, i.GuildID)

currentTime := time.Now()
cache.Update(cacheKey, currentTime)
err = cache.Update(cacheKey, currentTime)
if nil != err {
C.Logger().Err(err, fmt.Sprintf(
"Failed to store time of last command sync for guild \"%s\" in the cache.",
dgoGuild.ID))
}

finishWitSuccess(s, i, resp)
C.BotAuditLogger().Log(
Expand Down
5 changes: 4 additions & 1 deletion core_components/bot_webapi/command_api_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ func getCommandDTOs() []CommandDTO {
commands := C.SlashCommandManager().GetCommands()
commandDTOs := CommandDTOsFromCommands(commands)

cache.Update(CommandDTOsWebApiCacheKey, commandDTOs)
err := cache.Update(CommandDTOsWebApiCacheKey, commandDTOs)
if nil != err {
C.Logger().Warn(fmt.Sprintf("Failed to cache aggregated CommandDTOs: %s", err.Error()))
}

return commandDTOs
}
Expand Down
8 changes: 7 additions & 1 deletion core_components/bot_webapi/get_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,13 @@ func CommandGet(g *gin.Context) {
return
}

cache.Update(cacheKey, cmdDTO)
err := cache.Update(cacheKey, cmdDTO)
if nil != err {
C.Logger().Warn(fmt.Sprintf(
"Failed to cache web api response for CommandGet endpoint for command \"%s\": %s",
cmdID,
err.Error()))
}
}

g.JSON(http.StatusOK, cmdDTO)
Expand Down
17 changes: 17 additions & 0 deletions core_components/bot_webapi/get_components.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,16 @@ import (
"github.com/gin-gonic/gin"
"github.com/lazybytez/jojo-discord-bot/api"
"github.com/lazybytez/jojo-discord-bot/api/entities"
"github.com/lazybytez/jojo-discord-bot/services/cache"
"github.com/lazybytez/jojo-discord-bot/webapi"
"net/http"
"time"
)

// ComponentDTOsResponseWebApiCacheKey is the cache key used to store and retrieve all components
// as ComponentDTO instances from the cache.
const ComponentDTOsResponseWebApiCacheKey = "bot_web_api_components_get_cache"

// ComponentDTO is an intermediate data transfer object
// that can be output or received by the WebAPI.
// This type is used, because the bot both has the general api.Component
Expand Down Expand Up @@ -97,6 +102,13 @@ func ComponentDTOFromComponent(c *api.Component, guildId string) (ComponentDTO,
// @Failure 500 {object} webapi.ErrorResponse "An error indicating that an internal error happened"
// @Router /components [get]
func ComponentsGet(g *gin.Context) {
cachedResponse, ok := cache.Get(ComponentDTOsResponseWebApiCacheKey, []ComponentDTO{})
if ok {
g.JSON(http.StatusOK, cachedResponse)

return
}

componentDTOs := make([]ComponentDTO, len(api.Components))
var err error

Expand All @@ -116,5 +128,10 @@ func ComponentsGet(g *gin.Context) {
}
}

err = cache.Update(ComponentDTOsResponseWebApiCacheKey, componentDTOs)
if nil != err {
C.Logger().Err(err, "Failed to cache ComponentDTOs for GET components web api endpoint!")
}

g.JSON(http.StatusOK, componentDTOs)
}
33 changes: 33 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
version: "3.7"

services:
db:
image: bitnami/postgresql:14
environment:
- POSTGRES_USER=jojo
- POSTGRES_PASSWORD=jojo
- POSTGRES_DB=jojo
ports:
- "5432:5432"
volumes:
- db_data:/bitnami/postgresql
healthcheck:
test: [ "CMD", "pg_isready" ]
interval: 10s
timeout: 5s
retries: 5

redis:
image: bitnami/redis:7.0
environment:
REDIS_PASSWORD: "changeme123"
ports:
- "6379:6379"
healthcheck:
test: [ "CMD", "redis-cli", "ping" ]
interval: 1s
timeout: 3s
retries: 30

volumes:
db_data:
Loading

0 comments on commit f087bb9

Please sign in to comment.