Updated generic and hash package tests to use tcp connection instead of calling the handler directly

This commit is contained in:
Kelvin Clement Mwinuka
2024-05-24 13:40:40 +08:00
parent 43361cdd42
commit 926a008c23
9 changed files with 1655 additions and 1531 deletions

View File

@@ -16,7 +16,6 @@ package echovault
import (
"context"
"fmt"
"github.com/echovault/echovault/internal"
"slices"
"strings"
@@ -230,133 +229,133 @@ func (server *EchoVault) RewriteAOF() (string, error) {
// 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 {
server.commandsRWMut.Lock()
defer server.commandsRWMut.Unlock()
// Check if command already exists
for _, c := range server.commands {
if strings.EqualFold(c.Command, command.Command) {
return fmt.Errorf("command %s already exists", command.Command)
}
}
if command.SubCommand == nil || len(command.SubCommand) == 0 {
// Add command with no subcommands
server.commands = append(server.commands, internal.Command{
Command: command.Command,
Module: strings.ToLower(command.Module), // Convert module to lower case for uniformity
Categories: func() []string {
// Convert all the categories to lower case for uniformity
cats := make([]string, len(command.Categories))
for i, cat := range command.Categories {
cats[i] = strings.ToLower(cat)
}
return cats
}(),
Description: command.Description,
Sync: command.Sync,
KeyExtractionFunc: internal.KeyExtractionFunc(func(cmd []string) (internal.KeyExtractionFuncResult, error) {
accessKeys, err := command.KeyExtractionFunc(cmd)
if err != nil {
return internal.KeyExtractionFuncResult{}, err
}
return internal.KeyExtractionFuncResult{
Channels: []string{},
ReadKeys: accessKeys.ReadKeys,
WriteKeys: accessKeys.WriteKeys,
}, nil
}),
HandlerFunc: internal.HandlerFunc(func(params internal.HandlerFuncParams) ([]byte, error) {
return command.HandlerFunc(CommandHandlerFuncParams{
Context: params.Context,
Command: params.Command,
KeyLock: params.KeyLock,
KeyUnlock: params.KeyUnlock,
KeyRLock: params.KeyRLock,
KeyRUnlock: params.KeyRUnlock,
KeyExists: params.KeyExists,
CreateKeyAndLock: params.CreateKeyAndLock,
GetValue: params.GetValue,
SetValue: params.SetValue,
})
}),
})
return nil
}
// Add command with subcommands
newCommand := internal.Command{
Command: command.Command,
Module: command.Module,
Categories: func() []string {
// Convert all the categories to lower case for uniformity
cats := make([]string, len(command.Categories))
for j, cat := range command.Categories {
cats[j] = strings.ToLower(cat)
}
return cats
}(),
Description: command.Description,
Sync: command.Sync,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{}, nil
},
HandlerFunc: func(param internal.HandlerFuncParams) ([]byte, error) { return nil, nil },
SubCommands: make([]internal.SubCommand, len(command.SubCommand)),
}
for i, sc := range command.SubCommand {
// Skip the subcommand if it already exists in newCommand
if slices.ContainsFunc(newCommand.SubCommands, func(subcommand internal.SubCommand) bool {
return strings.EqualFold(subcommand.Command, sc.Command)
}) {
continue
}
newCommand.SubCommands[i] = internal.SubCommand{
Command: sc.Command,
Module: strings.ToLower(command.Module),
Categories: func() []string {
// Convert all the categories to lower case for uniformity
cats := make([]string, len(sc.Categories))
for j, cat := range sc.Categories {
cats[j] = strings.ToLower(cat)
}
return cats
}(),
Description: sc.Description,
Sync: sc.Sync,
KeyExtractionFunc: internal.KeyExtractionFunc(func(cmd []string) (internal.KeyExtractionFuncResult, error) {
accessKeys, err := sc.KeyExtractionFunc(cmd)
if err != nil {
return internal.KeyExtractionFuncResult{}, err
}
return internal.KeyExtractionFuncResult{
Channels: []string{},
ReadKeys: accessKeys.ReadKeys,
WriteKeys: accessKeys.WriteKeys,
}, nil
}),
HandlerFunc: internal.HandlerFunc(func(params internal.HandlerFuncParams) ([]byte, error) {
return sc.HandlerFunc(CommandHandlerFuncParams{
Context: params.Context,
Command: params.Command,
KeyLock: params.KeyLock,
KeyUnlock: params.KeyUnlock,
KeyRLock: params.KeyRLock,
KeyRUnlock: params.KeyRUnlock,
KeyExists: params.KeyExists,
CreateKeyAndLock: params.CreateKeyAndLock,
GetValue: params.GetValue,
SetValue: params.SetValue,
})
}),
}
}
server.commands = append(server.commands, newCommand)
return nil
}
// func (server *EchoVault) AddCommand(command CommandOptions) error {
// server.commandsRWMut.Lock()
// defer server.commandsRWMut.Unlock()
// // Check if command already exists
// for _, c := range server.commands {
// if strings.EqualFold(c.Command, command.Command) {
// return fmt.Errorf("command %s already exists", command.Command)
// }
// }
//
// if command.SubCommand == nil || len(command.SubCommand) == 0 {
// // Add command with no subcommands
// server.commands = append(server.commands, internal.Command{
// Command: command.Command,
// Module: strings.ToLower(command.Module), // Convert module to lower case for uniformity
// Categories: func() []string {
// // Convert all the categories to lower case for uniformity
// cats := make([]string, len(command.Categories))
// for i, cat := range command.Categories {
// cats[i] = strings.ToLower(cat)
// }
// return cats
// }(),
// Description: command.Description,
// Sync: command.Sync,
// KeyExtractionFunc: internal.KeyExtractionFunc(func(cmd []string) (internal.KeyExtractionFuncResult, error) {
// accessKeys, err := command.KeyExtractionFunc(cmd)
// if err != nil {
// return internal.KeyExtractionFuncResult{}, err
// }
// return internal.KeyExtractionFuncResult{
// Channels: []string{},
// ReadKeys: accessKeys.ReadKeys,
// WriteKeys: accessKeys.WriteKeys,
// }, nil
// }),
// HandlerFunc: internal.HandlerFunc(func(params internal.HandlerFuncParams) ([]byte, error) {
// return command.HandlerFunc(CommandHandlerFuncParams{
// Context: params.Context,
// Command: params.Command,
// KeyLock: params.KeyLock,
// KeyUnlock: params.KeyUnlock,
// KeyRLock: params.KeyRLock,
// KeyRUnlock: params.KeyRUnlock,
// KeyExists: params.KeyExists,
// CreateKeyAndLock: params.CreateKeyAndLock,
// GetValue: params.GetValue,
// SetValue: params.SetValue,
// })
// }),
// })
// return nil
// }
//
// // Add command with subcommands
// newCommand := internal.Command{
// Command: command.Command,
// Module: command.Module,
// Categories: func() []string {
// // Convert all the categories to lower case for uniformity
// cats := make([]string, len(command.Categories))
// for j, cat := range command.Categories {
// cats[j] = strings.ToLower(cat)
// }
// return cats
// }(),
// Description: command.Description,
// Sync: command.Sync,
// KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
// return internal.KeyExtractionFuncResult{}, nil
// },
// HandlerFunc: func(param internal.HandlerFuncParams) ([]byte, error) { return nil, nil },
// SubCommands: make([]internal.SubCommand, len(command.SubCommand)),
// }
//
// for i, sc := range command.SubCommand {
// // Skip the subcommand if it already exists in newCommand
// if slices.ContainsFunc(newCommand.SubCommands, func(subcommand internal.SubCommand) bool {
// return strings.EqualFold(subcommand.Command, sc.Command)
// }) {
// continue
// }
// newCommand.SubCommands[i] = internal.SubCommand{
// Command: sc.Command,
// Module: strings.ToLower(command.Module),
// Categories: func() []string {
// // Convert all the categories to lower case for uniformity
// cats := make([]string, len(sc.Categories))
// for j, cat := range sc.Categories {
// cats[j] = strings.ToLower(cat)
// }
// return cats
// }(),
// Description: sc.Description,
// Sync: sc.Sync,
// KeyExtractionFunc: internal.KeyExtractionFunc(func(cmd []string) (internal.KeyExtractionFuncResult, error) {
// accessKeys, err := sc.KeyExtractionFunc(cmd)
// if err != nil {
// return internal.KeyExtractionFuncResult{}, err
// }
// return internal.KeyExtractionFuncResult{
// Channels: []string{},
// ReadKeys: accessKeys.ReadKeys,
// WriteKeys: accessKeys.WriteKeys,
// }, nil
// }),
// HandlerFunc: internal.HandlerFunc(func(params internal.HandlerFuncParams) ([]byte, error) {
// return sc.HandlerFunc(CommandHandlerFuncParams{
// Context: params.Context,
// Command: params.Command,
// KeyLock: params.KeyLock,
// KeyUnlock: params.KeyUnlock,
// KeyRLock: params.KeyRLock,
// KeyRUnlock: params.KeyRUnlock,
// KeyExists: params.KeyExists,
// CreateKeyAndLock: params.CreateKeyAndLock,
// GetValue: params.GetValue,
// SetValue: params.SetValue,
// })
// }),
// }
// }
//
// server.commands = append(server.commands, newCommand)
//
// return nil
// }
// 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.

View File

@@ -29,14 +29,9 @@ import (
"github.com/echovault/echovault/internal/memberlist"
"github.com/echovault/echovault/internal/modules/acl"
"github.com/echovault/echovault/internal/modules/admin"
"github.com/echovault/echovault/internal/modules/connection"
"github.com/echovault/echovault/internal/modules/generic"
"github.com/echovault/echovault/internal/modules/hash"
"github.com/echovault/echovault/internal/modules/list"
"github.com/echovault/echovault/internal/modules/pubsub"
"github.com/echovault/echovault/internal/modules/set"
"github.com/echovault/echovault/internal/modules/sorted_set"
str "github.com/echovault/echovault/internal/modules/string"
"github.com/echovault/echovault/internal/raft"
"github.com/echovault/echovault/internal/snapshot"
"io"
@@ -139,12 +134,12 @@ func NewEchoVault(options ...func(echovault *EchoVault)) (*EchoVault, error) {
commands = append(commands, admin.Commands()...)
commands = append(commands, generic.Commands()...)
commands = append(commands, hash.Commands()...)
commands = append(commands, list.Commands()...)
commands = append(commands, connection.Commands()...)
commands = append(commands, pubsub.Commands()...)
commands = append(commands, set.Commands()...)
commands = append(commands, sorted_set.Commands()...)
commands = append(commands, str.Commands()...)
// commands = append(commands, list.Commands()...)
// commands = append(commands, connection.Commands()...)
// commands = append(commands, pubsub.Commands()...)
// commands = append(commands, set.Commands()...)
// commands = append(commands, sorted_set.Commands()...)
// commands = append(commands, str.Commands()...)
return commands
}(),
}
@@ -159,13 +154,14 @@ func NewEchoVault(options ...func(echovault *EchoVault)) (*EchoVault, error) {
)
// Load .so modules from config
for _, path := range echovault.config.Modules {
if err := echovault.LoadModule(path); err != nil {
log.Printf("%s %v\n", path, err)
continue
}
log.Printf("loaded plugin %s\n", path)
}
// TODO: Uncomment this
// for _, path := range echovault.config.Modules {
// if err := echovault.LoadModule(path); err != nil {
// log.Printf("%s %v\n", path, err)
// continue
// }
// log.Printf("loaded plugin %s\n", path)
// }
// Function for server commands retrieval
echovault.getCommands = func() []internal.Command {
@@ -190,35 +186,36 @@ func NewEchoVault(options ...func(echovault *EchoVault)) (*EchoVault, error) {
}
if echovault.isInCluster() {
echovault.raft = raft.NewRaft(raft.Opts{
Config: echovault.config,
GetCommand: echovault.getCommand,
SetValue: echovault.SetValue,
SetExpiry: echovault.SetExpiry,
DeleteKey: echovault.DeleteKey,
StartSnapshot: echovault.startSnapshot,
FinishSnapshot: echovault.finishSnapshot,
SetLatestSnapshotTime: echovault.setLatestSnapshot,
GetHandlerFuncParams: echovault.getHandlerFuncParams,
GetState: func() map[string]internal.KeyData {
state := make(map[string]internal.KeyData)
for k, v := range echovault.getState() {
if data, ok := v.(internal.KeyData); ok {
state[k] = data
}
}
return state
},
})
echovault.memberList = memberlist.NewMemberList(memberlist.Opts{
Config: echovault.config,
HasJoinedCluster: echovault.raft.HasJoinedCluster,
AddVoter: echovault.raft.AddVoter,
RemoveRaftServer: echovault.raft.RemoveServer,
IsRaftLeader: echovault.raft.IsRaftLeader,
ApplyMutate: echovault.raftApplyCommand,
ApplyDeleteKey: echovault.raftApplyDeleteKey,
})
// TODO: Uncomment this
// echovault.raft = raft.NewRaft(raft.Opts{
// Config: echovault.config,
// GetCommand: echovault.getCommand,
// SetValue: echovault.SetValue,
// SetExpiry: echovault.SetExpiry,
// DeleteKey: echovault.DeleteKey,
// StartSnapshot: echovault.startSnapshot,
// FinishSnapshot: echovault.finishSnapshot,
// SetLatestSnapshotTime: echovault.setLatestSnapshot,
// GetHandlerFuncParams: echovault.getHandlerFuncParams,
// GetState: func() map[string]internal.KeyData {
// state := make(map[string]internal.KeyData)
// for k, v := range echovault.getState() {
// if data, ok := v.(internal.KeyData); ok {
// state[k] = data
// }
// }
// return state
// },
// })
// echovault.memberList = memberlist.NewMemberList(memberlist.Opts{
// Config: echovault.config,
// HasJoinedCluster: echovault.raft.HasJoinedCluster,
// AddVoter: echovault.raft.AddVoter,
// RemoveRaftServer: echovault.raft.RemoveServer,
// IsRaftLeader: echovault.raft.IsRaftLeader,
// ApplyMutate: echovault.raftApplyCommand,
// ApplyDeleteKey: echovault.raftApplyDeleteKey,
// })
} else {
// Set up standalone snapshot engine
echovault.snapshotEngine = snapshot.NewSnapshotEngine(
@@ -241,10 +238,10 @@ func NewEchoVault(options ...func(echovault *EchoVault)) (*EchoVault, error) {
}),
snapshot.WithSetKeyDataFunc(func(key string, data internal.KeyData) {
ctx := context.Background()
if err := echovault.SetValue(ctx, key, data.Value); err != nil {
if err := echovault.setValues(ctx, map[string]interface{}{key: data.Value}); err != nil {
log.Println(err)
}
echovault.SetExpiry(ctx, key, data.ExpireAt, false)
echovault.setExpiry(ctx, key, data.ExpireAt, false)
}),
)
// Set up standalone AOF engine
@@ -265,10 +262,10 @@ func NewEchoVault(options ...func(echovault *EchoVault)) (*EchoVault, error) {
}),
aof.WithSetKeyDataFunc(func(key string, value internal.KeyData) {
ctx := context.Background()
if err := echovault.SetValue(ctx, key, value.Value); err != nil {
if err := echovault.setValues(ctx, map[string]interface{}{key: value.Value}); err != nil {
log.Println(err)
}
echovault.SetExpiry(ctx, key, value.ExpireAt, false)
echovault.setExpiry(ctx, key, value.ExpireAt, false)
}),
aof.WithHandleCommandFunc(func(command []byte) {
_, err := echovault.handleCommand(context.Background(), command, nil, true, false)

View File

@@ -113,9 +113,13 @@ func (server *EchoVault) setValues(ctx context.Context, entries map[string]inter
}
for key, value := range entries {
expireAt := time.Time{}
if _, ok := server.store[key]; ok {
expireAt = server.store[key].ExpireAt
}
server.store[key] = internal.KeyData{
Value: value,
ExpireAt: server.store[key].ExpireAt,
ExpireAt: expireAt,
}
if !server.isInCluster() {
server.snapshotEngine.IncrementChangeCount()

View File

@@ -102,14 +102,9 @@ func (server *EchoVault) LoadModule(path string, args ...string) error {
handlerFunc, ok := handlerFuncSymbol.(func(
ctx context.Context,
command []string,
keyExists func(ctx context.Context, key string) bool,
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),
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,
keysExist func(key []string) map[string]bool,
getValues func(ctx context.Context, key []string) map[string]interface{},
setValues func(ctx context.Context, entries map[string]interface{}) error,
args ...string,
) ([]byte, error))
if !ok {
@@ -151,14 +146,9 @@ func (server *EchoVault) LoadModule(path string, args ...string) error {
return handlerFunc(
params.Context,
params.Command,
params.KeyExists,
params.KeyLock,
params.KeyUnlock,
params.KeyRLock,
params.KeyRUnlock,
params.CreateKeyAndLock,
params.GetValue,
params.SetValue,
params.KeysExist,
params.GetValues,
params.SetValues,
args...,
)
},

View File

@@ -16,19 +16,13 @@ func createEchoVault() *EchoVault {
}
func presetValue(server *EchoVault, ctx context.Context, key string, value interface{}) error {
if _, err := server.CreateKeyAndLock(ctx, key); err != nil {
if err := server.setValues(ctx, map[string]interface{}{key: value}); err != nil {
return err
}
if err := server.SetValue(ctx, key, value); err != nil {
return err
}
server.KeyUnlock(ctx, key)
return nil
}
func presetKeyData(server *EchoVault, ctx context.Context, key string, data internal.KeyData) {
_, _ = server.CreateKeyAndLock(ctx, key)
defer server.KeyUnlock(ctx, key)
_ = server.SetValue(ctx, key, data.Value)
server.SetExpiry(ctx, key, data.ExpireAt, false)
_ = server.setValues(ctx, map[string]interface{}{key: data.Value})
server.setExpiry(ctx, key, data.ExpireAt, false)
}

View File

@@ -19,6 +19,7 @@ import (
"fmt"
"github.com/echovault/echovault/internal"
"github.com/echovault/echovault/internal/constants"
"log"
"strconv"
"strings"
"time"
@@ -36,6 +37,7 @@ func handleSet(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.WriteKeys[0]
keyExists := params.KeysExist(keys.WriteKeys)[key]
value := params.Command[2]
res := []byte(constants.OkResponse)
clock := params.GetClock()
@@ -48,41 +50,28 @@ func handleSet(params internal.HandlerFuncParams) ([]byte, error) {
// If Get is provided, the response should be the current stored value.
// If there's no current value, then the response should be nil.
if options.get {
if !params.KeyExists(params.Context, key) {
if !keyExists {
res = []byte("$-1\r\n")
} else {
res = []byte(fmt.Sprintf("+%v\r\n", params.GetValue(params.Context, key)))
res = []byte(fmt.Sprintf("+%v\r\n", params.GetValues(params.Context, []string{key})[key]))
}
}
if "xx" == strings.ToLower(options.exists) {
// If XX is specified, make sure the key exists.
if !params.KeyExists(params.Context, key) {
if !keyExists {
return nil, fmt.Errorf("key %s does not exist", key)
}
_, err = params.KeyLock(params.Context, key)
} else if "nx" == strings.ToLower(options.exists) {
// If NX is specified, make sure that the key does not currently exist.
if params.KeyExists(params.Context, key) {
if keyExists {
return nil, fmt.Errorf("key %s already exists", key)
}
_, err = params.CreateKeyAndLock(params.Context, key)
} else {
// Neither XX not NX are specified, lock or create the lock
if !params.KeyExists(params.Context, key) {
// Key does not exist, create it
_, err = params.CreateKeyAndLock(params.Context, key)
} else {
// Key exists, acquire the lock
_, err = params.KeyLock(params.Context, key)
}
}
if err != nil {
return nil, err
}
defer params.KeyUnlock(params.Context, key)
if err = params.SetValue(params.Context, key, internal.AdaptType(value)); err != nil {
if err = params.SetValues(params.Context, map[string]interface{}{
key: internal.AdaptType(value),
}); err != nil {
return nil, err
}
@@ -100,53 +89,19 @@ func handleMSet(params internal.HandlerFuncParams) ([]byte, error) {
return nil, err
}
entries := make(map[string]KeyObject)
// Release all acquired key locks
defer func() {
for k, v := range entries {
if v.locked {
params.KeyUnlock(params.Context, k)
entries[k] = KeyObject{
value: v.value,
locked: false,
}
}
}
}()
entries := make(map[string]interface{})
// Extract all the key/value pairs
for i, key := range params.Command[1:] {
if i%2 == 0 {
entries[key] = KeyObject{
value: internal.AdaptType(params.Command[1:][i+1]),
locked: false,
entries[key] = internal.AdaptType(params.Command[1:][i+1])
}
}
}
// Acquire all the locks for each key first
// If any key cannot be acquired, abandon transaction and release all currently held keys
for k, v := range entries {
if params.KeyExists(params.Context, k) {
if _, err := params.KeyLock(params.Context, k); err != nil {
return nil, err
}
entries[k] = KeyObject{value: v.value, locked: true}
continue
}
if _, err := params.CreateKeyAndLock(params.Context, k); err != nil {
return nil, err
}
entries[k] = KeyObject{value: v.value, locked: true}
}
// Set all the values
for k, v := range entries {
if err := params.SetValue(params.Context, k, v.value); err != nil {
if err = params.SetValues(params.Context, entries); err != nil {
return nil, err
}
}
return []byte(constants.OkResponse), nil
}
@@ -157,18 +112,13 @@ func handleGet(params internal.HandlerFuncParams) ([]byte, error) {
return nil, err
}
key := keys.ReadKeys[0]
keyExists := params.KeysExist([]string{key})[key]
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte("$-1\r\n"), nil
}
_, err = params.KeyRLock(params.Context, key)
if err != nil {
return nil, err
}
defer params.KeyRUnlock(params.Context, key)
value := params.GetValue(params.Context, key)
value := params.GetValues(params.Context, []string{key})[key]
return []byte(fmt.Sprintf("+%v\r\n", value)), nil
}
@@ -180,34 +130,12 @@ func handleMGet(params internal.HandlerFuncParams) ([]byte, error) {
}
values := make(map[string]string)
locks := make(map[string]bool)
for _, key := range keys.ReadKeys {
if _, ok := values[key]; ok {
// Skip if we have already locked this key
continue
}
if params.KeyExists(params.Context, key) {
_, err = params.KeyRLock(params.Context, key)
if err != nil {
return nil, fmt.Errorf("could not obtain lock for %s key", key)
}
locks[key] = true
continue
}
for key, value := range params.GetValues(params.Context, keys.ReadKeys) {
if value == nil {
values[key] = ""
continue
}
defer func() {
for key, locked := range locks {
if locked {
params.KeyRUnlock(params.Context, key)
locks[key] = false
}
}
}()
for key, _ := range locks {
values[key] = fmt.Sprintf("%v", params.GetValue(params.Context, key))
values[key] = fmt.Sprintf("%v", value)
}
bytes := []byte(fmt.Sprintf("*%d\r\n", len(params.Command[1:])))
@@ -229,10 +157,13 @@ func handleDel(params internal.HandlerFuncParams) ([]byte, error) {
return nil, err
}
count := 0
for _, key := range keys.WriteKeys {
err = params.DeleteKey(params.Context, key)
for key, exists := range params.KeysExist(keys.WriteKeys) {
if !exists {
continue
}
err = params.DeleteKey(key)
if err != nil {
// log.Printf("could not delete key %s due to error: %+v\n", key, err) // TODO: Uncomment this
log.Printf("could not delete key %s due to error: %+v\n", key, err)
continue
}
count += 1
@@ -247,17 +178,13 @@ func handlePersist(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.WriteKeys[0]
keyExists := params.KeysExist(keys.WriteKeys)[key]
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte(":0\r\n"), nil
}
if _, err = params.KeyLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyUnlock(params.Context, key)
expireAt := params.GetExpiry(params.Context, key)
expireAt := params.GetExpiry(key)
if expireAt == (time.Time{}) {
return []byte(":0\r\n"), nil
}
@@ -274,17 +201,13 @@ func handleExpireTime(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.ReadKeys[0]
keyExists := params.KeysExist(keys.ReadKeys)[key]
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte(":-2\r\n"), nil
}
if _, err = params.KeyRLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyRUnlock(params.Context, key)
expireAt := params.GetExpiry(params.Context, key)
expireAt := params.GetExpiry(key)
if expireAt == (time.Time{}) {
return []byte(":-1\r\n"), nil
@@ -305,19 +228,15 @@ func handleTTL(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.ReadKeys[0]
keyExists := params.KeysExist(keys.ReadKeys)[key]
clock := params.GetClock()
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte(":-2\r\n"), nil
}
if _, err = params.KeyRLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyRUnlock(params.Context, key)
expireAt := params.GetExpiry(params.Context, key)
expireAt := params.GetExpiry(key)
if expireAt == (time.Time{}) {
return []byte(":-1\r\n"), nil
@@ -342,6 +261,7 @@ func handleExpire(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.WriteKeys[0]
keyExists := params.KeysExist(keys.WriteKeys)[key]
// Extract time
n, err := strconv.ParseInt(params.Command[2], 10, 64)
@@ -353,21 +273,16 @@ func handleExpire(params internal.HandlerFuncParams) ([]byte, error) {
expireAt = params.GetClock().Now().Add(time.Duration(n) * time.Millisecond)
}
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte(":0\r\n"), nil
}
if _, err = params.KeyLock(params.Context, key); err != nil {
return []byte(":0\r\n"), err
}
defer params.KeyUnlock(params.Context, key)
if len(params.Command) == 3 {
params.SetExpiry(params.Context, key, expireAt, true)
return []byte(":1\r\n"), nil
}
currentExpireAt := params.GetExpiry(params.Context, key)
currentExpireAt := params.GetExpiry(key)
switch strings.ToLower(params.Command[3]) {
case "nx":
@@ -410,6 +325,7 @@ func handleExpireAt(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.WriteKeys[0]
keyExists := params.KeysExist(keys.WriteKeys)[key]
// Extract time
n, err := strconv.ParseInt(params.Command[2], 10, 64)
@@ -421,21 +337,16 @@ func handleExpireAt(params internal.HandlerFuncParams) ([]byte, error) {
expireAt = time.UnixMilli(n)
}
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte(":0\r\n"), nil
}
if _, err = params.KeyLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyUnlock(params.Context, key)
if len(params.Command) == 3 {
params.SetExpiry(params.Context, key, expireAt, true)
return []byte(":1\r\n"), nil
}
currentExpireAt := params.GetExpiry(params.Context, key)
currentExpireAt := params.GetExpiry(key)
switch strings.ToLower(params.Command[3]) {
case "nx":

File diff suppressed because it is too large Load Diff

View File

@@ -32,6 +32,7 @@ func handleHSET(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.WriteKeys[0]
keyExists := params.KeysExist(keys.WriteKeys)[key]
entries := make(map[string]interface{})
if len(params.Command[2:])%2 != 0 {
@@ -42,26 +43,19 @@ func handleHSET(params internal.HandlerFuncParams) ([]byte, error) {
entries[params.Command[i]] = internal.AdaptType(params.Command[i+1])
}
if !params.KeyExists(params.Context, key) {
_, err = params.CreateKeyAndLock(params.Context, key)
if !keyExists {
if err != nil {
return nil, err
}
defer params.KeyUnlock(params.Context, key)
if err = params.SetValue(params.Context, key, entries); err != nil {
if err = params.SetValues(params.Context, map[string]interface{}{key: entries}); err != nil {
return nil, err
}
return []byte(fmt.Sprintf(":%d\r\n", len(entries))), nil
}
if _, err = params.KeyLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyUnlock(params.Context, key)
hash, ok := params.GetValue(params.Context, key).(map[string]interface{})
hash, ok := params.GetValues(params.Context, []string{key})[key].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("value at %s is not a hash", key)
hash = make(map[string]interface{})
}
count := 0
@@ -76,7 +70,7 @@ func handleHSET(params internal.HandlerFuncParams) ([]byte, error) {
hash[field] = value
count += 1
}
if err = params.SetValue(params.Context, key, hash); err != nil {
if err = params.SetValues(params.Context, map[string]interface{}{key: hash}); err != nil {
return nil, err
}
@@ -90,18 +84,14 @@ func handleHGET(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.ReadKeys[0]
keyExists := params.KeysExist(keys.ReadKeys)[key]
fields := params.Command[2:]
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte("$-1\r\n"), nil
}
if _, err = params.KeyRLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyRUnlock(params.Context, key)
hash, ok := params.GetValue(params.Context, key).(map[string]interface{})
hash, ok := params.GetValues(params.Context, []string{key})[key].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("value at %s is not a hash", key)
}
@@ -141,18 +131,14 @@ func handleHSTRLEN(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.ReadKeys[0]
keyExists := params.KeysExist(keys.ReadKeys)[key]
fields := params.Command[2:]
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte("$-1\r\n"), nil
}
if _, err = params.KeyRLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyRUnlock(params.Context, key)
hash, ok := params.GetValue(params.Context, key).(map[string]interface{})
hash, ok := params.GetValues(params.Context, []string{key})[key].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("value at %s is not a hash", key)
}
@@ -192,17 +178,13 @@ func handleHVALS(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.ReadKeys[0]
keyExists := params.KeysExist(keys.ReadKeys)[key]
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte("*0\r\n"), nil
}
if _, err = params.KeyRLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyRUnlock(params.Context, key)
hash, ok := params.GetValue(params.Context, key).(map[string]interface{})
hash, ok := params.GetValues(params.Context, []string{key})[key].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("value at %s is not a hash", key)
}
@@ -233,6 +215,7 @@ func handleHRANDFIELD(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.ReadKeys[0]
keyExists := params.KeysExist(keys.ReadKeys)[key]
count := 1
if len(params.Command) >= 3 {
@@ -255,16 +238,11 @@ func handleHRANDFIELD(params internal.HandlerFuncParams) ([]byte, error) {
}
}
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte("*0\r\n"), nil
}
if _, err = params.KeyRLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyRUnlock(params.Context, key)
hash, ok := params.GetValue(params.Context, key).(map[string]interface{})
hash, ok := params.GetValues(params.Context, []string{key})[key].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("value at %s is not a hash", key)
}
@@ -349,17 +327,13 @@ func handleHLEN(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.ReadKeys[0]
keyExists := params.KeysExist(keys.ReadKeys)[key]
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte(":0\r\n"), nil
}
if _, err = params.KeyRLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyRUnlock(params.Context, key)
hash, ok := params.GetValue(params.Context, key).(map[string]interface{})
hash, ok := params.GetValues(params.Context, []string{key})[key].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("value at %s is not a hash", key)
}
@@ -374,17 +348,13 @@ func handleHKEYS(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.ReadKeys[0]
keyExists := params.KeysExist(keys.ReadKeys)[key]
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte("*0\r\n"), nil
}
if _, err = params.KeyRLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyRUnlock(params.Context, key)
hash, ok := params.GetValue(params.Context, key).(map[string]interface{})
hash, ok := params.GetValues(params.Context, []string{key})[key].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("value at %s is not a hash", key)
}
@@ -404,6 +374,7 @@ func handleHINCRBY(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.WriteKeys[0]
keyExists := params.KeysExist(keys.WriteKeys)[key]
field := params.Command[2]
var intIncrement int
@@ -423,33 +394,24 @@ func handleHINCRBY(params internal.HandlerFuncParams) ([]byte, error) {
intIncrement = i
}
if !params.KeyExists(params.Context, key) {
if _, err := params.CreateKeyAndLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyUnlock(params.Context, key)
if !keyExists {
hash := make(map[string]interface{})
if strings.EqualFold(params.Command[0], "hincrbyfloat") {
hash[field] = floatIncrement
if err = params.SetValue(params.Context, key, hash); err != nil {
if err = params.SetValues(params.Context, map[string]interface{}{key: hash}); err != nil {
return nil, err
}
return []byte(fmt.Sprintf("+%s\r\n", strconv.FormatFloat(floatIncrement, 'f', -1, 64))), nil
} else {
hash[field] = intIncrement
if err = params.SetValue(params.Context, key, hash); err != nil {
if err = params.SetValues(params.Context, map[string]interface{}{key: hash}); err != nil {
return nil, err
}
return []byte(fmt.Sprintf(":%d\r\n", intIncrement)), nil
}
}
if _, err := params.KeyLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyUnlock(params.Context, key)
hash, ok := params.GetValue(params.Context, key).(map[string]interface{})
hash, ok := params.GetValues(params.Context, []string{key})[key].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("value at %s is not a hash", key)
}
@@ -477,7 +439,7 @@ func handleHINCRBY(params internal.HandlerFuncParams) ([]byte, error) {
}
}
if err = params.SetValue(params.Context, key, hash); err != nil {
if err = params.SetValues(params.Context, map[string]interface{}{key: hash}); err != nil {
return nil, err
}
@@ -496,17 +458,13 @@ func handleHGETALL(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.ReadKeys[0]
keyExists := params.KeysExist(keys.ReadKeys)[key]
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte("*0\r\n"), nil
}
if _, err = params.KeyRLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyRUnlock(params.Context, key)
hash, ok := params.GetValue(params.Context, key).(map[string]interface{})
hash, ok := params.GetValues(params.Context, []string{key})[key].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("value at %s is not a hash", key)
}
@@ -536,18 +494,14 @@ func handleHEXISTS(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.ReadKeys[0]
keyExists := params.KeysExist(keys.ReadKeys)[key]
field := params.Command[2]
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte(":0\r\n"), nil
}
if _, err = params.KeyRLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyRUnlock(params.Context, key)
hash, ok := params.GetValue(params.Context, key).(map[string]interface{})
hash, ok := params.GetValues(params.Context, []string{key})[key].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("value at %s is not a hash", key)
}
@@ -566,18 +520,14 @@ func handleHDEL(params internal.HandlerFuncParams) ([]byte, error) {
}
key := keys.WriteKeys[0]
keyExists := params.KeysExist(keys.WriteKeys)[key]
fields := params.Command[2:]
if !params.KeyExists(params.Context, key) {
if !keyExists {
return []byte(":0\r\n"), nil
}
if _, err = params.KeyLock(params.Context, key); err != nil {
return nil, err
}
defer params.KeyUnlock(params.Context, key)
hash, ok := params.GetValue(params.Context, key).(map[string]interface{})
hash, ok := params.GetValues(params.Context, []string{key})[key].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("value at %s is not a hash", key)
}
@@ -591,7 +541,7 @@ func handleHDEL(params internal.HandlerFuncParams) ([]byte, error) {
}
}
if err = params.SetValue(params.Context, key, hash); err != nil {
if err = params.SetValues(params.Context, map[string]interface{}{key: hash}); err != nil {
return nil, err
}

File diff suppressed because it is too large Load Diff