Skip to content
7 changes: 6 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22.x'
go-version-file: 'go.mod'
- name: Install dependencies
run: go mod vendor
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: latest
args: --timeout=5m
- name: Test with Go
run: go test -json > TestResults.json
- name: Upload Go test results
Expand Down
162 changes: 162 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# golangci-lint configuration
# Documentation: https://golangci-lint.run/usage/configuration/

run:
timeout: 5m
tests: true
modules-download-mode: readonly

# Output configuration
output:
formats:
- format: colored-line-number
print-issued-lines: true
print-linter-name: true
sort-results: true

linters:
disable-all: true
enable:
# Enabled by default
- errcheck # Checks for unchecked errors
- gosimple # Simplify code
- govet # Reports suspicious constructs
- ineffassign # Detects ineffectual assignments
- staticcheck # Staticcheck is a go vet on steroids
- unused # Checks for unused constants, variables, functions and types

# Additional recommended linters
- gofmt # Checks whether code was gofmt-ed
- goimports # Check import statements are formatted according to goimport command
- misspell # Finds commonly misspelled English words
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go
- gocyclo # Computes cyclomatic complexities
- goconst # Finds repeated strings that could be replaced by a constant
- gosec # Inspects source code for security problems
- unconvert # Remove unnecessary type conversions
- unparam # Reports unused function parameters
- nakedret # Finds naked returns in functions greater than a specified length
- gocognit # Computes cognitive complexities
- godot # Check if comments end in a period
- whitespace # Detection of leading and trailing whitespace
- gci # Controls Go package import order and makes it deterministic

linters-settings:
goimports:
# Use goimports as the formatter
local-prefixes: github.com/dcbickfo/redcache

gofmt:
# Simplify code: gofmt with `-s` option
simplify: true

gocyclo:
# Minimal cyclomatic complexity to report
min-complexity: 15

gocognit:
# Minimal cognitive complexity to report
min-complexity: 15

goconst:
# Minimal length of string constant
min-len: 3
# Minimum occurrences to report
min-occurrences: 3

gosec:
# Exclude some checks
excludes:
- G104 # Audit errors not checked (we use errcheck for this)

revive:
confidence: 0.8
rules:
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unused-parameter
- name: unreachable-code
- name: redefines-builtin-id

nakedret:
# Make an issue if func has more lines of code than this setting and has naked return
max-func-lines: 30

unparam:
# Check exported functions
check-exported: false

whitespace:
multi-if: false
multi-func: false

gci:
# Section configuration to compare against
sections:
- standard # Standard section: captures all standard packages
- default # Default section: contains all imports that could not be matched to another section type
- prefix(github.com/dcbickfo/redcache) # Custom section: groups all imports with the specified Prefix

issues:
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
# Exclude some linters from running on tests files
- path: _test\.go
linters:
- gocyclo
- gocognit
- errcheck
- gosec
- unparam
- revive
- goconst
- godot
- whitespace
- gci

# Exclude known issues in vendor
- path: vendor/
linters:
- all

# Ignore "new" parameter name shadowing built-in
- text: "redefines-builtin-id"
linters:
- revive

# Ignore integer overflow in CRC16 - this is intentional and safe
- text: "G115.*integer overflow"
path: internal/cmdx/slot.go
linters:
- gosec

# Maximum issues count per one linter
max-issues-per-linter: 50

# Maximum count of issues with the same text
max-same-issues: 3

# Show only new issues
new: false

# Fix found issues (if it's supported by the linter)
fix: false
50 changes: 28 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ package main
import (
"context"
"database/sql"
"github.com/google/go-cmp/cmp/internal/value"
"log"
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The imported 'log' package is not used in the example code. Consider removing this unused import.

Copilot uses AI. Check for mistakes.
"time"

"github.com/redis/rueidis"
Expand All @@ -20,15 +20,15 @@ import (
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
}

func run() error {
var db sql.DB
var db *sql.DB
// initialize db
client, err := redcache.NewRedCacheAside(
rueidis.ClientOption{
InitAddress: addr,
InitAddress: []string{"127.0.0.1:6379"},
},
redcache.CacheAsideOption{
LockTTL: time.Second * 1,
Expand All @@ -37,31 +37,33 @@ func run() error {
if err != nil {
return err
}

repo := Repository{
client: client,
db: &db,
}
val, err := Repository.GetByID(context.Background(), "key")

val, err := repo.GetByID(context.Background(), "key")
if err != nil {
return err
return err
}
vals, err := Repository.GetByIDs(context.Background(), map[string]string{"key1": "val1", "key2": "val2"})

vals, err := repo.GetByIDs(context.Background(), []string{"key1", "key2"})
if err != nil {
return err
}
_, _ = val, vals
return nil
}

type Repository struct {
client redcache.CacheAside
client *redcache.CacheAside
db *sql.DB
}

func (r Repository) GetByID(ctx context.Context, key string) (string, error) {
val, err := r.client.Get(ctx, time.Minute, key, func(ctx context.Context, key string) (val string, err error) {
if err = db.QueryRowContext(ctx, "SELECT val FROM mytab WHERE id = ?", key).Scan(&val); err == sql.ErrNoRows {
if err = r.db.QueryRowContext(ctx, "SELECT val FROM mytab WHERE id = ?", key).Scan(&val); err == sql.ErrNoRows {
val = "NULL" // cache null to avoid penetration.
err = nil // clear err in case of sql.ErrNoRows.
}
Expand All @@ -72,33 +74,37 @@ func (r Repository) GetByID(ctx context.Context, key string) (string, error) {
} else if val == "NULL" {
val = ""
err = sql.ErrNoRows
}
// ...
}
return val, err
}

func (r Repository) GetByIDs(ctx context.Context, key []string) (map[string]string, error) {
val, err := r.client.GetMulti(ctx, time.Minute, key, func(ctx context.Context, key []string) (val map[string]string, err error) {
rows := db.QueryContext(ctx, "SELECT id, val FROM mytab WHERE id = ?", key)
func (r Repository) GetByIDs(ctx context.Context, keys []string) (map[string]string, error) {
val, err := r.client.GetMulti(ctx, time.Minute, keys, func(ctx context.Context, keys []string) (val map[string]string, err error) {
val = make(map[string]string)
rows, err := r.db.QueryContext(ctx, "SELECT id, val FROM mytab WHERE id IN (?)", keys)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var id, rowVal string
if err = rows.Scan(&id, &rowVal); err != nil {
return
return nil, err
}
val[id] = rowVal
}
if len(val) != len(key) {
for _, k := range key {
if len(val) != len(keys) {
for _, k := range keys {
if _, ok := val[k]; !ok {
val[k] = "NULL" // cache null to avoid penetration.
}
}
}
return
return val, nil
})
if err != nil {
return nil, err
}
}
// handle any NULL vals if desired
// ...

Expand Down
Loading
Loading