Added godoc commends for embedded methods and types to extend commands. Moved constants to internal directory

This commit is contained in:
Kelvin Clement Mwinuka
2024-04-29 15:20:10 +08:00
parent dcb88ffead
commit 281c4f27a7
37 changed files with 187 additions and 75 deletions

View File

@@ -35,7 +35,28 @@ type CommandListOptions struct {
MODULE string
}
// TODO: Write godoc comment for CommandOptions type
// CommandOptions provides the specification of the command to be added to the EchoVault instance.
//
// Command is the keyword used to trigger this command (e.g. LPUSH, ZADD, ACL ...).
//
// Module is a string that classifies a group of commands.
//
// Categories is a string slice of all the categories that this command belongs to.
//
// Description is a string describing the command, can include an example of how to trigger the command.
//
// SubCommand is a slice of subcommands for this command.
//
// Sync is a boolean value that determines whether this command should be synced across a replication cluster.
// If subcommands are specified, each subcommand will override this value for its own execution.
//
// KeyExtractionFunc is a function that extracts the keys from the command if the command accesses any keys.
// the extracted keys are used by the ACL layer to determine whether a TCP client is authorized to execute this command.
// If subcommands are specified, this function is discarded and each subcommands must implement its own KeyExtractionFunc.
//
// HandlerFunc is the command handler. This function must return a valid RESP2 response as it the command will be
// available to RESP clients. If subcommands are specified, this function is discarded and each subcommand must implement
// its own HandlerFunc.
type CommandOptions struct {
Command string
Module string
@@ -43,19 +64,36 @@ type CommandOptions struct {
Description string
SubCommand []SubCommandOptions
Sync bool
KeyExtractionFunc types.PluginKeyExtractionFunc
HandlerFunc types.PluginHandlerFunc
KeyExtractionFunc types.CommandKeyExtractionFunc
HandlerFunc types.CommandHandlerFunc
}
// TODO: Write godoc comment for SubCommandOptions type
// SubCommandOptions provides the specification of a subcommand within CommandOptions.
//
// Command is the keyword used to trigger this subcommand (e.g. "CAT" for the subcommand "ACL CAT").
//
// Module is a string that classifies a group of commands/subcommands.
//
// Categories is a string slice of all the categories that this subcommand belongs to.
//
// Description is a string describing the subcommand, can include an example of how to trigger the subcommand.
//
// Sync is a boolean value that determines whether this subcommand should be synced across a replication cluster.
// This value overrides the Sync value set by the parent command. It's possible to have some synced and un-synced
// subcommands with the same parent command regardless of the parent's Sync value.
//
// KeyExtractionFunc is a function that extracts the keys from the subcommand if it accesses any keys.
//
// HandlerFunc is the subcommand handler. This function must return a valid RESP2 response as it will be
// available to RESP clients.
type SubCommandOptions struct {
Command string
Module string
Categories []string
Description string
Sync bool
KeyExtractionFunc types.PluginKeyExtractionFunc
HandlerFunc types.PluginHandlerFunc
KeyExtractionFunc types.CommandKeyExtractionFunc
HandlerFunc types.CommandHandlerFunc
}
// CommandList returns the list of commands currently loaded in the EchoVault instance.
@@ -123,7 +161,15 @@ func (server *EchoVault) RewriteAOF() (string, error) {
return internal.ParseStringResponse(b)
}
// TODO: Write godoc comment for AddCommand method
// AddCommand adds a new command to EchoVault. The added command can be executed using the ExecuteCommand method.
//
// Parameters:
//
// `command` - CommandOptions.
//
// Errors:
//
// "command <command> already exists" - If a command with the same command name as the passed command already exists.
func (server *EchoVault) AddCommand(command CommandOptions) error {
// Check if command already exists
for _, c := range server.commands {
@@ -159,7 +205,7 @@ func (server *EchoVault) AddCommand(command CommandOptions) error {
}, nil
}),
HandlerFunc: internal.HandlerFunc(func(params internal.HandlerFuncParams) ([]byte, error) {
return command.HandlerFunc(types.PluginHandlerFuncParams{
return command.HandlerFunc(types.CommandHandlerFuncParams{
Context: params.Context,
Command: params.Command,
Connection: params.Connection,
@@ -171,10 +217,6 @@ func (server *EchoVault) AddCommand(command CommandOptions) error {
CreateKeyAndLock: params.CreateKeyAndLock,
GetValue: params.GetValue,
SetValue: params.SetValue,
GetExpiry: params.GetExpiry,
SetExpiry: params.SetExpiry,
RemoveExpiry: params.RemoveExpiry,
DeleteKey: params.DeleteKey,
})
}),
})
@@ -234,7 +276,7 @@ func (server *EchoVault) AddCommand(command CommandOptions) error {
}, nil
}),
HandlerFunc: internal.HandlerFunc(func(params internal.HandlerFuncParams) ([]byte, error) {
return sc.HandlerFunc(types.PluginHandlerFuncParams{
return sc.HandlerFunc(types.CommandHandlerFuncParams{
Context: params.Context,
Command: params.Command,
Connection: params.Connection,
@@ -246,10 +288,6 @@ func (server *EchoVault) AddCommand(command CommandOptions) error {
CreateKeyAndLock: params.CreateKeyAndLock,
GetValue: params.GetValue,
SetValue: params.SetValue,
GetExpiry: params.GetExpiry,
SetExpiry: params.SetExpiry,
RemoveExpiry: params.RemoveExpiry,
DeleteKey: params.DeleteKey,
})
}),
}
@@ -260,12 +298,47 @@ func (server *EchoVault) AddCommand(command CommandOptions) error {
return nil
}
// TODO: Write godoc comment for ExecuteCommand method
func (server *EchoVault) ExecuteCommand(command []string) ([]byte, error) {
// ExecuteCommand executes the command passed to it. If 1 string is passed, EchoVault will try to
// execute the command. If 2 strings are passed, EchoVault will attempt to execute the subcommand of the command.
// If more than 2 strings are provided, all additional strings will be ignored.
//
// This method returns the raw RESP response from the command handler. You will have to parse the RESP response if
// you want to use the return value from the handler.
//
// This method does not work with handlers that manipulate the client connection directly (i.e SUBSCRIBE, PSUBSCRIBE).
// If you'd like to (p)subscribe or (p)unsubscribe, use the (P)SUBSCRIBE and (P)UNSUBSCRIBE methods instead.
//
// Parameters:
//
// `command` - ...string.
//
// Returns: []byte - Raw RESP response returned by the command handler.
//
// Errors:
//
// All errors from the command handler are forwarded to the caller. Other errors returned include:
//
// "command <command> not supported" - If the command does not exist.
//
// "command <command> <subcommand> not supported" - If the command exists but the subcommand does not exist for that command.
func (server *EchoVault) ExecuteCommand(command ...string) ([]byte, error) {
return server.handleCommand(server.context, internal.EncodeCommand(command), nil, false, true)
}
// TODO: Write godoc commend for RemoveCommand method
// RemoveCommand removes the specified command or subcommand from EchoVault.
// When commands are removed, they will no longer be available for both the embedded instance and for TCP clients.
//
// Note: If a command is removed, the API wrapper for the command will also be unusable.
// For example, calling RemoveCommand("LPUSH") will cause the LPUSH method to always return a
// "command LPUSH not supported" error so use this method with caution.
//
// If one string is passed, the command matching that string is removed along will all of its subcommand if it has any.
// If two strings are passed, only the subcommand of the specified command is removed.
// If more than 2 strings are passed, all additional strings are ignored.
//
// Parameters:
//
// `command` - ...string.
func (server *EchoVault) RemoveCommand(command ...string) {
switch len(command) {
case 1:

View File

@@ -20,11 +20,11 @@ import (
"crypto/x509"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/aof"
"github.com/echovault/echovault/internal/clock"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/echovault/echovault/internal/eviction"
"github.com/echovault/echovault/internal/memberlist"
"github.com/echovault/echovault/internal/modules/acl"

View File

@@ -18,8 +18,8 @@ import (
"context"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"log"
"math/rand"
"runtime"

View File

@@ -18,8 +18,8 @@ import (
"context"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"net"
"strings"
)

View File

@@ -19,8 +19,8 @@ import (
"errors"
"flag"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"log"
"os"
"path"

View File

@@ -1,7 +1,7 @@
package config
import (
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal/constants"
"time"
)

View File

@@ -20,9 +20,9 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/gobwas/glob"
"gopkg.in/yaml.v3"
"log"

View File

@@ -18,8 +18,8 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"gopkg.in/yaml.v3"
"log"
"os"

View File

@@ -17,8 +17,8 @@ package admin
import (
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"github.com/gobwas/glob"
"slices"
"strings"

View File

@@ -17,8 +17,8 @@ package connection
import (
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
)
func handlePing(params internal.HandlerFuncParams) ([]byte, error) {

View File

@@ -17,8 +17,8 @@ package generic
import (
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"log"
"strconv"
"strings"

View File

@@ -16,8 +16,8 @@ package generic
import (
"errors"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
)
func setKeyFunc(cmd []string) (internal.KeyExtractionFuncResult, error) {

View File

@@ -17,8 +17,8 @@ package hash
import (
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"math/rand"
"slices"
"strconv"

View File

@@ -16,8 +16,8 @@ package hash
import (
"errors"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
)
func hsetKeyFunc(cmd []string) (internal.KeyExtractionFuncResult, error) {

View File

@@ -17,8 +17,8 @@ package list
import (
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"math"
"slices"
"strings"

View File

@@ -16,8 +16,8 @@ package list
import (
"errors"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
)
func lpushKeyFunc(cmd []string) (internal.KeyExtractionFuncResult, error) {

View File

@@ -17,8 +17,8 @@ package pubsub
import (
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"strings"
)

View File

@@ -17,8 +17,8 @@ package set
import (
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"slices"
"strings"
)

View File

@@ -16,8 +16,8 @@ package set
import (
"errors"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"slices"
"strings"
)

View File

@@ -18,8 +18,8 @@ import (
"cmp"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"math"
"slices"
"strconv"

View File

@@ -16,8 +16,8 @@ package sorted_set
import (
"errors"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"slices"
"strings"
)

View File

@@ -17,8 +17,8 @@ package str
import (
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
)
func handleSetRange(params internal.HandlerFuncParams) ([]byte, error) {

View File

@@ -16,8 +16,8 @@ package str
import (
"errors"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
)
func setRangeKeyFunc(cmd []string) (internal.KeyExtractionFuncResult, error) {

View File

@@ -20,7 +20,7 @@ import (
"cmp"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/internal/constants"
"io"
"log"
"math/big"

View File

@@ -17,9 +17,9 @@ package acl
import (
"crypto/sha256"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/echovault"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/echovault/echovault/internal/modules/acl"
"github.com/tidwall/resp"
"net"

View File

@@ -18,9 +18,9 @@ import (
"bytes"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/echovault"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/echovault/echovault/types"
"github.com/tidwall/resp"
"strconv"
@@ -64,16 +64,16 @@ Test command to handle successful addition of a single command without subcomman
The value passed must be an integer.`,
Categories: []string{},
Sync: false,
KeyExtractionFunc: func(cmd []string) (types.PluginKeyExtractionFuncResult, error) {
KeyExtractionFunc: func(cmd []string) (types.CommandKeyExtractionFuncResult, error) {
if len(cmd) != 4 {
return types.PluginKeyExtractionFuncResult{}, errors.New(constants.WrongArgsResponse)
return types.CommandKeyExtractionFuncResult{}, errors.New(constants.WrongArgsResponse)
}
return types.PluginKeyExtractionFuncResult{
return types.CommandKeyExtractionFuncResult{
WriteKeys: cmd[1:2],
ReadKeys: cmd[2:3],
}, nil
},
HandlerFunc: func(params types.PluginHandlerFuncParams) ([]byte, error) {
HandlerFunc: func(params types.CommandHandlerFuncParams) ([]byte, error) {
if len(params.Command) != 4 {
return nil, errors.New(constants.WrongArgsResponse)
}
@@ -128,16 +128,16 @@ Test command to handle successful addition of a single command with subcommands.
The value passed must be an integer.`,
Categories: []string{},
Sync: false,
KeyExtractionFunc: func(cmd []string) (types.PluginKeyExtractionFuncResult, error) {
KeyExtractionFunc: func(cmd []string) (types.CommandKeyExtractionFuncResult, error) {
if len(cmd) != 5 {
return types.PluginKeyExtractionFuncResult{}, errors.New(constants.WrongArgsResponse)
return types.CommandKeyExtractionFuncResult{}, errors.New(constants.WrongArgsResponse)
}
return types.PluginKeyExtractionFuncResult{
return types.CommandKeyExtractionFuncResult{
WriteKeys: cmd[2:3],
ReadKeys: cmd[3:4],
}, nil
},
HandlerFunc: func(params types.PluginHandlerFuncParams) ([]byte, error) {
HandlerFunc: func(params types.CommandHandlerFuncParams) ([]byte, error) {
if len(params.Command) != 5 {
return nil, errors.New(constants.WrongArgsResponse)
}
@@ -187,7 +187,7 @@ The value passed must be an integer.`,
t.Errorf("AddCommand() error = %v, wantErr %v", err, tt.wantErr)
}
for _, scenario := range tt.scenarios {
b, err := server.ExecuteCommand(scenario.command)
b, err := server.ExecuteCommand(scenario.command...)
if scenario.wantErr != nil {
if scenario.wantErr.Error() != err.Error() {
t.Errorf("AddCommand() error = %v, wantErr %v", err, scenario.wantErr)
@@ -243,7 +243,7 @@ func TestEchoVault_ExecuteCommand(t *testing.T) {
if tt.args.presetValue != nil {
_, _ = server.LPush(tt.args.key, tt.args.presetValue...)
}
b, err := server.ExecuteCommand(tt.args.command)
b, err := server.ExecuteCommand(tt.args.command...)
if tt.wantErr != nil {
if err.Error() != tt.wantErr.Error() {
t.Errorf("ExecuteCommand() error = %v, wantErr %v", err, tt.wantErr)
@@ -298,7 +298,7 @@ func TestEchoVault_RemoveCommand(t *testing.T) {
server := createEchoVault()
t.Run(tt.name, func(t *testing.T) {
server.RemoveCommand(tt.args.removeCommand...)
_, err := server.ExecuteCommand(tt.args.executeCommand)
_, err := server.ExecuteCommand(tt.args.executeCommand...)
if tt.wantErr != nil {
if err.Error() != tt.wantErr.Error() {
t.Errorf("RemoveCommand() error = %v, wantErr %v", err, tt.wantErr)

View File

@@ -18,10 +18,10 @@ import (
"bytes"
"context"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/echovault"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/tidwall/resp"
"net"
"reflect"

View File

@@ -18,10 +18,10 @@ import (
"bytes"
"context"
"errors"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/echovault"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/tidwall/resp"
"net"
"reflect"

View File

@@ -19,11 +19,11 @@ import (
"context"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/echovault"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/clock"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/tidwall/resp"
"net"
"reflect"

View File

@@ -19,10 +19,10 @@ import (
"context"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/echovault"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/tidwall/resp"
"net"
"reflect"

View File

@@ -19,10 +19,10 @@ import (
"context"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/echovault"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/tidwall/resp"
"net"
"reflect"

View File

@@ -18,10 +18,10 @@ import (
"bytes"
"context"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/echovault"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/echovault/echovault/internal/modules/pubsub"
"github.com/tidwall/resp"
"net"

View File

@@ -19,10 +19,10 @@ import (
"context"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/echovault"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/echovault/echovault/internal/modules/set"
"github.com/tidwall/resp"
"net"

View File

@@ -19,10 +19,10 @@ import (
"context"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/echovault"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/echovault/echovault/internal/modules/sorted_set"
"github.com/tidwall/resp"
"math"

View File

@@ -19,10 +19,10 @@ import (
"context"
"errors"
"fmt"
"github.com/echovault/echovault/constants"
"github.com/echovault/echovault/echovault"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/config"
"github.com/echovault/echovault/internal/constants"
"github.com/tidwall/resp"
"net"
"reflect"

View File

@@ -35,27 +35,66 @@ type EchoVault interface {
DeleteKey(ctx context.Context, key string) error
}
type PluginKeyExtractionFuncResult struct {
// CommandKeyExtractionFuncResult specifies the keys accessed by the associated command or subcommand.
// ReadKeys is a string slice containing the keys that the commands read from.
// WriteKeys is a string slice containing the keys that the command writes to.
//
// These keys will typically be extracted from the command slice, but they can also be hardcoded.
type CommandKeyExtractionFuncResult struct {
ReadKeys []string
WriteKeys []string
}
type PluginKeyExtractionFunc func(cmd []string) (PluginKeyExtractionFuncResult, error)
type PluginHandlerFunc func(params PluginHandlerFuncParams) ([]byte, error)
type PluginHandlerFuncParams struct {
// CommandKeyExtractionFunc if the function that extracts the keys accessed by the command or subcommand.
type CommandKeyExtractionFunc func(cmd []string) (CommandKeyExtractionFuncResult, error)
// CommandHandlerFunc is the handler function for the command or subcommand.
//
// This function must return a byte slice containing a valid RESP2 response, or an error.
type CommandHandlerFunc func(params CommandHandlerFuncParams) ([]byte, error)
// CommandHandlerFuncParams contains the helper parameters passed to the command's handler by EchoVault.
//
// Command is the string slice command containing the command that triggered this handler.
//
// Connection is the TCP connection that triggered this command. In embedded mode, this will always be nil.
// Any TCP client that trigger the custom command will have its connection passed to the handler here.
//
// KeyExists returns true if the key passed to it exists in the store.
//
// CreateKeyAndLock creates the new key and immediately write locks it. If the key already exists, then
// it is simply write locked which makes this function safe to call even if the key already exists. Always call
// KeyUnlock when done after CreateKeyAndLock.
//
// KeyLock acquires a write lock for the specified key. If the lock is successfully acquired, the function will return
// (true, nil). Otherwise, it will return false and an error describing why the locking failed. Always call KeyUnlock
// when done after KeyLock.
//
// KeyUnlock releases the write lock for the specified key. Always call this after KeyLock otherwise the key will not be
// lockable by any future invocations of this command or other commands.
//
// KeyRLock acquires a read lock for the specified key. If the lock is successfully acquired, the function will return
// (true, nil). Otherwise, it will return false and an error describing why the locking failed. Always call KeyRUnlock
// when done after KeyRLock.
//
// KeyRUnlock releases the real lock for the specified key. Always call this after KeyRLock otherwise the key will not be
// write-lockable by any future invocations of this command or other commands.
//
// GetValue returns the value held at the specified key as an interface{}. Make sure to invoke KeyLock or KeyRLock on the
// key before GetValue to ensure thread safety.
//
// SetValue sets the value at the specified key. Make sure to invoke KeyLock on the key before
// SetValue to ensure thread safety.
type CommandHandlerFuncParams struct {
Context context.Context
Command []string
Connection *net.Conn
KeyExists func(ctx context.Context, key string) bool
CreateKeyAndLock func(ctx context.Context, key string) (bool, error)
KeyLock func(ctx context.Context, key string) (bool, error)
KeyUnlock func(ctx context.Context, key string)
KeyRLock func(ctx context.Context, key string) (bool, error)
KeyRUnlock func(ctx context.Context, key string)
KeyExists func(ctx context.Context, key string) bool
CreateKeyAndLock func(ctx context.Context, key string) (bool, error)
GetValue func(ctx context.Context, key string) interface{}
SetValue func(ctx context.Context, key string, value interface{}) error
GetExpiry func(ctx context.Context, key string) time.Time
SetExpiry func(ctx context.Context, key string, expire time.Time, touch bool)
RemoveExpiry func(ctx context.Context, key string)
DeleteKey func(ctx context.Context, key string) error
}