Added DEL command for deleting keys from the store

This commit is contained in:
Kelvin Mwinuka
2024-03-08 22:49:03 +08:00
parent e34ed74e3f
commit 0b90c9343e
6 changed files with 280 additions and 226 deletions

View File

@@ -22,7 +22,7 @@ services:
- MTLS=false - MTLS=false
- BOOTSTRAP_CLUSTER=false - BOOTSTRAP_CLUSTER=false
- ACL_CONFIG=/etc/config/echovault/acl.yml - ACL_CONFIG=/etc/config/echovault/acl.yml
- REQUIRE_PASS=true - REQUIRE_PASS=false
- PASSWORD=default_password - PASSWORD=default_password
- FORWARD_COMMAND=false - FORWARD_COMMAND=false
- SNAPSHOT_THRESHOLD=1000 - SNAPSHOT_THRESHOLD=1000
@@ -47,206 +47,216 @@ services:
networks: networks:
- testnet - testnet
# cluster_node_1: cluster_node_1:
# container_name: cluster_node_1 container_name: cluster_node_1
# build: build:
# context: . context: .
# dockerfile: Dockerfile.dev dockerfile: Dockerfile.dev
# environment: environment:
# - PORT=7480 - PORT=7480
# - RAFT_PORT=8000 - RAFT_PORT=8000
# - ML_PORT=7946 - ML_PORT=7946
# - KEY=/generic/ssl/certs/echovault/server1.key - KEY=/generic/ssl/certs/echovault/server1.key
# - CERT=/generic/ssl/certs/echovault/server1.crt - CERT=/generic/ssl/certs/echovault/server1.crt
# - SERVER_ID=1 - SERVER_ID=1
# - DATA_DIR=/var/lib/echovault - DATA_DIR=/var/lib/echovault
# - IN_MEMORY=false - IN_MEMORY=false
# - TLS=true - TLS=false
# - MTLS=true - MTLS=false
# - BOOTSTRAP_CLUSTER=true - BOOTSTRAP_CLUSTER=true
# - ACL_CONFIG=/generic/config/echovault/acl.yml - ACL_CONFIG=/generic/config/echovault/acl.yml
# - REQUIRE_PASS=false - REQUIRE_PASS=false
# - FORWARD_COMMAND=true - FORWARD_COMMAND=true
# - SNAPSHOT_THRESHOLD=1000 - SNAPSHOT_THRESHOLD=1000
# - SNAPSHOT_INTERVAL=5m30s - SNAPSHOT_INTERVAL=5m30s
# - RESTORE_SNAPSHOT=false - RESTORE_SNAPSHOT=false
# - RESTORE_AOF=false - RESTORE_AOF=false
# - AOF_SYNC_STRATEGY=everysec - AOF_SYNC_STRATEGY=everysec
# # List of server cert/key pairs - MAX_MEMORY=2000kb
# - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key - EVICTION_POLICY=allkeys-lfu
# - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key # List of server cert/key pairs
# # List of client certificate authorities - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key
# - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key
# ports: # List of client certificate authorities
# - "7481:7480" - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt
# - "7945:7946" ports:
# - "8000:8000" - "7481:7480"
# volumes: - "7945:7946"
# - ./config/acl.yml:/generic/config/echovault/acl.yml - "8000:8000"
# - ./volumes/cluster_node_1:/var/lib/echovault volumes:
# networks: - ./config/acl.yml:/generic/config/echovault/acl.yml
# - testnet - ./volumes/cluster_node_1:/var/lib/echovault
# networks:
# cluster_node_2: - testnet
# container_name: cluster_node_2
# build: cluster_node_2:
# context: . container_name: cluster_node_2
# dockerfile: Dockerfile.dev build:
# environment: context: .
# - PORT=7480 dockerfile: Dockerfile.dev
# - RAFT_PORT=8000 environment:
# - ML_PORT=7946 - PORT=7480
# - KEY=/generic/ssl/certs/echovault/server1.key - RAFT_PORT=8000
# - CERT=/generic/ssl/certs/echovault/server1.crt - ML_PORT=7946
# - SERVER_ID=2 - KEY=/generic/ssl/certs/echovault/server1.key
# - JOIN_ADDR=cluster_node_1:7946 - CERT=/generic/ssl/certs/echovault/server1.crt
# - DATA_DIR=/var/lib/echovault - SERVER_ID=2
# - IN_MEMORY=false - JOIN_ADDR=cluster_node_1:7946
# - TLS=true - DATA_DIR=/var/lib/echovault
# - MTLS=true - IN_MEMORY=false
# - BOOTSTRAP_CLUSTER=false - TLS=false
# - ACL_CONFIG=/generic/config/echovault/acl.yml - MTLS=false
# - REQUIRE_PASS=false - BOOTSTRAP_CLUSTER=false
# - FORWARD_COMMAND=true - ACL_CONFIG=/generic/config/echovault/acl.yml
# - SNAPSHOT_THRESHOLD=1000 - REQUIRE_PASS=false
# - SNAPSHOT_INTERVAL=5m30s - FORWARD_COMMAND=true
# - RESTORE_SNAPSHOT=false - SNAPSHOT_THRESHOLD=1000
# - RESTORE_AOF=false - SNAPSHOT_INTERVAL=5m30s
# - AOF_SYNC_STRATEGY=everysec - RESTORE_SNAPSHOT=false
# # List of server cert/key pairs - RESTORE_AOF=false
# - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key - AOF_SYNC_STRATEGY=everysec
# - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key - MAX_MEMORY=2000kb
# # List of client certificate authorities - EVICTION_POLICY=allkeys-lfu
# - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt # List of server cert/key pairs
# ports: - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key
# - "7482:7480" - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key
# - "7947:7946" # List of client certificate authorities
# - "8001:8000" - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt
# volumes: ports:
# - ./config/acl.yml:/generic/config/echovault/acl.yml - "7482:7480"
# - ./volumes/cluster_node_2:/var/lib/echovault - "7947:7946"
# networks: - "8001:8000"
# - testnet volumes:
# - ./config/acl.yml:/generic/config/echovault/acl.yml
# cluster_node_3: - ./volumes/cluster_node_2:/var/lib/echovault
# container_name: cluster_node_3 networks:
# build: - testnet
# context: .
# dockerfile: Dockerfile.dev cluster_node_3:
# environment: container_name: cluster_node_3
# - PORT=7480 build:
# - RAFT_PORT=8000 context: .
# - ML_PORT=7946 dockerfile: Dockerfile.dev
# - KEY=/generic/ssl/certs/echovault/server1.key environment:
# - CERT=/generic/ssl/certs/echovault/server1.crt - PORT=7480
# - SERVER_ID=3 - RAFT_PORT=8000
# - JOIN_ADDR=cluster_node_1:7946 - ML_PORT=7946
# - DATA_DIR=/var/lib/echovault - KEY=/generic/ssl/certs/echovault/server1.key
# - IN_MEMORY=false - CERT=/generic/ssl/certs/echovault/server1.crt
# - TLS=true - SERVER_ID=3
# - MTLS=true - JOIN_ADDR=cluster_node_1:7946
# - BOOTSTRAP_CLUSTER=false - DATA_DIR=/var/lib/echovault
# - ACL_CONFIG=/generic/config/echovault/acl.yml - IN_MEMORY=false
# - REQUIRE_PASS=false - TLS=false
# - FORWARD_COMMAND=true - MTLS=false
# - SNAPSHOT_THRESHOLD=1000 - BOOTSTRAP_CLUSTER=false
# - SNAPSHOT_INTERVAL=5m30s - ACL_CONFIG=/generic/config/echovault/acl.yml
# - RESTORE_SNAPSHOT=false - REQUIRE_PASS=false
# - RESTORE_AOF=false - FORWARD_COMMAND=true
# - AOF_SYNC_STRATEGY=everysec - SNAPSHOT_THRESHOLD=1000
# # List of server cert/key pairs - SNAPSHOT_INTERVAL=5m30s
# - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key - RESTORE_SNAPSHOT=false
# - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key - RESTORE_AOF=false
# # List of client certificate authorities - AOF_SYNC_STRATEGY=everysec
# - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt - MAX_MEMORY=2000kb
# ports: - EVICTION_POLICY=allkeys-lfu
# - "7483:7480" # List of server cert/key pairs
# - "7948:7946" - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key
# - "8002:8000" - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key
# volumes: # List of client certificate authorities
# - ./config/acl.yml:/generic/config/echovault/acl.yml - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt
# - ./volumes/cluster_node_3:/var/lib/echovault ports:
# networks: - "7483:7480"
# - testnet - "7948:7946"
# - "8002:8000"
# cluster_node_4: volumes:
# container_name: cluster_node_4 - ./config/acl.yml:/generic/config/echovault/acl.yml
# build: - ./volumes/cluster_node_3:/var/lib/echovault
# context: . networks:
# dockerfile: Dockerfile.dev - testnet
# environment:
# - PORT=7480 cluster_node_4:
# - RAFT_PORT=8000 container_name: cluster_node_4
# - ML_PORT=7946 build:
# - KEY=/generic/ssl/certs/echovault/server1.key context: .
# - CERT=/generic/ssl/certs/echovault/server1.crt dockerfile: Dockerfile.dev
# - SERVER_ID=4 environment:
# - JOIN_ADDR=cluster_node_1:7946 - PORT=7480
# - DATA_DIR=/var/lib/echovault - RAFT_PORT=8000
# - IN_MEMORY=false - ML_PORT=7946
# - TLS=true - KEY=/generic/ssl/certs/echovault/server1.key
# - MTLS=true - CERT=/generic/ssl/certs/echovault/server1.crt
# - BOOTSTRAP_CLUSTER=false - SERVER_ID=4
# - ACL_CONFIG=/generic/config/echovault/acl.yml - JOIN_ADDR=cluster_node_1:7946
# - REQUIRE_PASS=false - DATA_DIR=/var/lib/echovault
# - FORWARD_COMMAND=true - IN_MEMORY=false
# - SNAPSHOT_THRESHOLD=1000 - TLS=false
# - SNAPSHOT_INTERVAL=5m30s - MTLS=false
# - RESTORE_SNAPSHOT=false - BOOTSTRAP_CLUSTER=false
# - RESTORE_AOF=false - ACL_CONFIG=/generic/config/echovault/acl.yml
# - AOF_SYNC_STRATEGY=everysec - REQUIRE_PASS=false
# # List of server cert/key pairs - FORWARD_COMMAND=true
# - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key - SNAPSHOT_THRESHOLD=1000
# - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key - SNAPSHOT_INTERVAL=5m30s
# # List of client certificate authorities - RESTORE_SNAPSHOT=false
# - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt - RESTORE_AOF=false
# ports: - AOF_SYNC_STRATEGY=everysec
# - "7484:7480" - MAX_MEMORY=2000kb
# - "7949:7946" - EVICTION_POLICY=allkeys-lfu
# - "8003:8000" # List of server cert/key pairs
# volumes: - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key
# - ./config/acl.yml:/generic/config/echovault/acl.yml - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key
# - ./volumes/cluster_node_4:/var/lib/echovault # List of client certificate authorities
# networks: - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt
# - testnet ports:
# - "7484:7480"
# cluster_node_5: - "7949:7946"
# container_name: cluster_node_5 - "8003:8000"
# build: volumes:
# context: . - ./config/acl.yml:/generic/config/echovault/acl.yml
# dockerfile: Dockerfile.dev - ./volumes/cluster_node_4:/var/lib/echovault
# environment: networks:
# - PORT=7480 - testnet
# - RAFT_PORT=8000
# - ML_PORT=7946 cluster_node_5:
# - KEY=/generic/ssl/certs/echovault/server1.key container_name: cluster_node_5
# - CERT=/generic/ssl/certs/echovault/server1.crt build:
# - SERVER_ID=5 context: .
# - JOIN_ADDR=cluster_node_1:7946 dockerfile: Dockerfile.dev
# - DATA_DIR=/var/lib/echovault environment:
# - IN_MEMORY=false - PORT=7480
# - TLS=true - RAFT_PORT=8000
# - MTLS=true - ML_PORT=7946
# - BOOTSTRAP_CLUSTER=false - KEY=/generic/ssl/certs/echovault/server1.key
# - ACL_CONFIG=/generic/config/echovault/acl.yml - CERT=/generic/ssl/certs/echovault/server1.crt
# - REQUIRE_PASS=false - SERVER_ID=5
# - FORWARD_COMMAND=true - JOIN_ADDR=cluster_node_1:7946
# - SNAPSHOT_THRESHOLD=1000 - DATA_DIR=/var/lib/echovault
# - SNAPSHOT_INTERVAL=5m30s - IN_MEMORY=false
# - RESTORE_SNAPSHOT=false - TLS=false
# - RESTORE_AOF=false - MTLS=false
# - AOF_SYNC_STRATEGY=everysec - BOOTSTRAP_CLUSTER=false
# # List of server cert/key pairs - ACL_CONFIG=/generic/config/echovault/acl.yml
# - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key - REQUIRE_PASS=false
# - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key - FORWARD_COMMAND=true
# # List of client certificate authorities - SNAPSHOT_THRESHOLD=1000
# - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt - SNAPSHOT_INTERVAL=5m30s
# ports: - RESTORE_SNAPSHOT=false
# - "7485:7480" - RESTORE_AOF=false
# - "7950:7946" - AOF_SYNC_STRATEGY=everysec
# - "8004:8000" - MAX_MEMORY=2000kb
# volumes: - EVICTION_POLICY=allkeys-lfu
# - ./config/acl.yml:/generic/config/echovault/acl.yml # List of server cert/key pairs
# - ./volumes/cluster_node_5:/var/lib/echovault - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key
# networks: - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key
# - testnet # List of client certificate authorities
- CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt
ports:
- "7485:7480"
- "7950:7946"
- "8004:8000"
volumes:
- ./config/acl.yml:/generic/config/echovault/acl.yml
- ./volumes/cluster_node_5:/var/lib/echovault
networks:
- testnet

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/echovault/echovault/src/utils" "github.com/echovault/echovault/src/utils"
"log"
"net" "net"
"strings" "strings"
"time" "time"
@@ -206,6 +207,23 @@ func handleMGet(ctx context.Context, cmd []string, server utils.Server, _ *net.C
return bytes, nil return bytes, nil
} }
func handleDel(ctx context.Context, cmd []string, server utils.Server, _ *net.Conn) ([]byte, error) {
keys, err := delKeyFunc(cmd)
if err != nil {
return nil, err
}
count := 0
for _, key := range keys {
err = server.DeleteKey(ctx, key)
if err != nil {
log.Printf("could not delete key %s due to error: %+v\n", key, err)
continue
}
count += 1
}
return []byte(fmt.Sprintf(":%d\r\n", count)), nil
}
func Commands() []utils.Command { func Commands() []utils.Command {
return []utils.Command{ return []utils.Command{
{ {
@@ -249,5 +267,13 @@ PXAT - Expire at the exat time in unix milliseconds (positive integer).`,
KeyExtractionFunc: mgetKeyFunc, KeyExtractionFunc: mgetKeyFunc,
HandlerFunc: handleMGet, HandlerFunc: handleMGet,
}, },
{
Command: "del",
Categories: []string{utils.KeyspaceCategory, utils.WriteCategory, utils.SlowCategory},
Description: "(DEL) Removes one or more keys from the store.",
Sync: true,
KeyExtractionFunc: delKeyFunc,
HandlerFunc: handleDel,
},
} }
} }

View File

@@ -38,3 +38,10 @@ func mgetKeyFunc(cmd []string) ([]string, error) {
} }
return cmd[1:], nil return cmd[1:], nil
} }
func delKeyFunc(cmd []string) ([]string, error) {
if len(cmd) < 2 {
return nil, errors.New(utils.WrongArgsResponse)
}
return cmd[1:], nil
}

View File

@@ -185,6 +185,10 @@ func (server *Server) DeleteKey(ctx context.Context, key string) error {
// updateKeyInCache updates either the key access count or the most recent access time in the cache // updateKeyInCache updates either the key access count or the most recent access time in the cache
// depending on whether an LFU or LRU strategy was used. // depending on whether an LFU or LRU strategy was used.
func (server *Server) updateKeyInCache(ctx context.Context, key string) error { func (server *Server) updateKeyInCache(ctx context.Context, key string) error {
// Only update cache when in standalone mode or when raft leader
if server.IsInCluster() || (server.IsInCluster() && !server.raft.IsRaftLeader()) {
return nil
}
// If max memory is 0, there's no max so no need to update caches // If max memory is 0, there's no max so no need to update caches
if server.Config.MaxMemory == 0 { if server.Config.MaxMemory == 0 {
return nil return nil

View File

@@ -57,7 +57,7 @@ func (server *Server) handleCommand(ctx context.Context, message []byte, conn *n
} }
} }
// If we're not in cluster mode and command/subcommand is a write command, wait for state copy to finish. // If the command is a write command, wait for state copy to finish.
if utils.IsWriteCommand(command, subCommand) { if utils.IsWriteCommand(command, subCommand) {
for { for {
if !server.StateCopyInProgress.Load() { if !server.StateCopyInProgress.Load() {

View File

@@ -138,26 +138,6 @@ func NewServer(opts Opts) *Server {
) )
} }
// Set up LFU cache
server.lfuCache = struct {
mutex sync.Mutex
cache eviction.CacheLFU
}{
mutex: sync.Mutex{},
cache: eviction.NewCacheLFU(),
}
// set up LRU cache
server.lruCache = struct {
mutex sync.Mutex
cache eviction.CacheLRU
}{
mutex: sync.Mutex{},
cache: eviction.NewCacheLRU(),
}
// TODO: If eviction policy is volatile-ttl, start goroutine that continuously reads the mem stats
// TODO: before triggering purge once max-memory is reached
return server return server
} }
@@ -319,7 +299,13 @@ func (server *Server) Start(ctx context.Context) {
// Initialise raft and memberlist // Initialise raft and memberlist
server.raft.RaftInit(ctx) server.raft.RaftInit(ctx)
server.memberList.MemberListInit(ctx) server.memberList.MemberListInit(ctx)
} else { if server.raft.IsRaftLeader() {
server.InitialiseCaches()
}
}
if !server.IsInCluster() {
server.InitialiseCaches()
// Restore from AOF by default if it's enabled // Restore from AOF by default if it's enabled
if conf.RestoreAOF { if conf.RestoreAOF {
err := server.AOFEngine.Restore() err := server.AOFEngine.Restore()
@@ -406,3 +392,24 @@ func (server *Server) ShutDown(ctx context.Context) {
server.memberList.MemberListShutdown(ctx) server.memberList.MemberListShutdown(ctx)
} }
} }
func (server *Server) InitialiseCaches() {
// Set up LFU cache
server.lfuCache = struct {
mutex sync.Mutex
cache eviction.CacheLFU
}{
mutex: sync.Mutex{},
cache: eviction.NewCacheLFU(),
}
// set up LRU cache
server.lruCache = struct {
mutex sync.Mutex
cache eviction.CacheLRU
}{
mutex: sync.Mutex{},
cache: eviction.NewCacheLRU(),
}
// TODO: If eviction policy is volatile-ttl, start goroutine that continuously reads the mem stats
// TODO: before triggering purge once max-memory is reached
}