diff --git a/docker-compose.yaml b/docker-compose.yaml index e281624..e80559c 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -22,7 +22,7 @@ services: - MTLS=false - BOOTSTRAP_CLUSTER=false - ACL_CONFIG=/etc/config/echovault/acl.yml - - REQUIRE_PASS=true + - REQUIRE_PASS=false - PASSWORD=default_password - FORWARD_COMMAND=false - SNAPSHOT_THRESHOLD=1000 @@ -47,206 +47,216 @@ services: networks: - testnet -# cluster_node_1: -# container_name: cluster_node_1 -# build: -# context: . -# dockerfile: Dockerfile.dev -# environment: -# - PORT=7480 -# - RAFT_PORT=8000 -# - ML_PORT=7946 -# - KEY=/generic/ssl/certs/echovault/server1.key -# - CERT=/generic/ssl/certs/echovault/server1.crt -# - SERVER_ID=1 -# - DATA_DIR=/var/lib/echovault -# - IN_MEMORY=false -# - TLS=true -# - MTLS=true -# - BOOTSTRAP_CLUSTER=true -# - ACL_CONFIG=/generic/config/echovault/acl.yml -# - REQUIRE_PASS=false -# - FORWARD_COMMAND=true -# - SNAPSHOT_THRESHOLD=1000 -# - SNAPSHOT_INTERVAL=5m30s -# - RESTORE_SNAPSHOT=false -# - RESTORE_AOF=false -# - AOF_SYNC_STRATEGY=everysec -# # List of server cert/key pairs -# - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key -# - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key -# # List of client certificate authorities -# - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt -# ports: -# - "7481:7480" -# - "7945:7946" -# - "8000:8000" -# volumes: -# - ./config/acl.yml:/generic/config/echovault/acl.yml -# - ./volumes/cluster_node_1:/var/lib/echovault -# networks: -# - testnet -# -# cluster_node_2: -# container_name: cluster_node_2 -# build: -# context: . -# dockerfile: Dockerfile.dev -# environment: -# - PORT=7480 -# - RAFT_PORT=8000 -# - ML_PORT=7946 -# - KEY=/generic/ssl/certs/echovault/server1.key -# - CERT=/generic/ssl/certs/echovault/server1.crt -# - SERVER_ID=2 -# - JOIN_ADDR=cluster_node_1:7946 -# - DATA_DIR=/var/lib/echovault -# - IN_MEMORY=false -# - TLS=true -# - MTLS=true -# - BOOTSTRAP_CLUSTER=false -# - ACL_CONFIG=/generic/config/echovault/acl.yml -# - REQUIRE_PASS=false -# - FORWARD_COMMAND=true -# - SNAPSHOT_THRESHOLD=1000 -# - SNAPSHOT_INTERVAL=5m30s -# - RESTORE_SNAPSHOT=false -# - RESTORE_AOF=false -# - AOF_SYNC_STRATEGY=everysec -# # List of server cert/key pairs -# - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key -# - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key -# # List of client certificate authorities -# - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt -# ports: -# - "7482:7480" -# - "7947:7946" -# - "8001:8000" -# volumes: -# - ./config/acl.yml:/generic/config/echovault/acl.yml -# - ./volumes/cluster_node_2:/var/lib/echovault -# networks: -# - testnet -# -# cluster_node_3: -# container_name: cluster_node_3 -# build: -# context: . -# dockerfile: Dockerfile.dev -# environment: -# - PORT=7480 -# - RAFT_PORT=8000 -# - ML_PORT=7946 -# - KEY=/generic/ssl/certs/echovault/server1.key -# - CERT=/generic/ssl/certs/echovault/server1.crt -# - SERVER_ID=3 -# - JOIN_ADDR=cluster_node_1:7946 -# - DATA_DIR=/var/lib/echovault -# - IN_MEMORY=false -# - TLS=true -# - MTLS=true -# - BOOTSTRAP_CLUSTER=false -# - ACL_CONFIG=/generic/config/echovault/acl.yml -# - REQUIRE_PASS=false -# - FORWARD_COMMAND=true -# - SNAPSHOT_THRESHOLD=1000 -# - SNAPSHOT_INTERVAL=5m30s -# - RESTORE_SNAPSHOT=false -# - RESTORE_AOF=false -# - AOF_SYNC_STRATEGY=everysec -# # List of server cert/key pairs -# - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key -# - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key -# # List of client certificate authorities -# - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt -# ports: -# - "7483:7480" -# - "7948:7946" -# - "8002:8000" -# volumes: -# - ./config/acl.yml:/generic/config/echovault/acl.yml -# - ./volumes/cluster_node_3:/var/lib/echovault -# networks: -# - testnet -# -# cluster_node_4: -# container_name: cluster_node_4 -# build: -# context: . -# dockerfile: Dockerfile.dev -# environment: -# - PORT=7480 -# - RAFT_PORT=8000 -# - ML_PORT=7946 -# - KEY=/generic/ssl/certs/echovault/server1.key -# - CERT=/generic/ssl/certs/echovault/server1.crt -# - SERVER_ID=4 -# - JOIN_ADDR=cluster_node_1:7946 -# - DATA_DIR=/var/lib/echovault -# - IN_MEMORY=false -# - TLS=true -# - MTLS=true -# - BOOTSTRAP_CLUSTER=false -# - ACL_CONFIG=/generic/config/echovault/acl.yml -# - REQUIRE_PASS=false -# - FORWARD_COMMAND=true -# - SNAPSHOT_THRESHOLD=1000 -# - SNAPSHOT_INTERVAL=5m30s -# - RESTORE_SNAPSHOT=false -# - RESTORE_AOF=false -# - AOF_SYNC_STRATEGY=everysec -# # List of server cert/key pairs -# - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key -# - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key -# # List of client certificate authorities -# - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt -# ports: -# - "7484:7480" -# - "7949:7946" -# - "8003:8000" -# volumes: -# - ./config/acl.yml:/generic/config/echovault/acl.yml -# - ./volumes/cluster_node_4:/var/lib/echovault -# networks: -# - testnet -# -# cluster_node_5: -# container_name: cluster_node_5 -# build: -# context: . -# dockerfile: Dockerfile.dev -# environment: -# - PORT=7480 -# - RAFT_PORT=8000 -# - ML_PORT=7946 -# - KEY=/generic/ssl/certs/echovault/server1.key -# - CERT=/generic/ssl/certs/echovault/server1.crt -# - SERVER_ID=5 -# - JOIN_ADDR=cluster_node_1:7946 -# - DATA_DIR=/var/lib/echovault -# - IN_MEMORY=false -# - TLS=true -# - MTLS=true -# - BOOTSTRAP_CLUSTER=false -# - ACL_CONFIG=/generic/config/echovault/acl.yml -# - REQUIRE_PASS=false -# - FORWARD_COMMAND=true -# - SNAPSHOT_THRESHOLD=1000 -# - SNAPSHOT_INTERVAL=5m30s -# - RESTORE_SNAPSHOT=false -# - RESTORE_AOF=false -# - AOF_SYNC_STRATEGY=everysec -# # List of server cert/key pairs -# - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key -# - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key -# # 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 \ No newline at end of file + cluster_node_1: + container_name: cluster_node_1 + build: + context: . + dockerfile: Dockerfile.dev + environment: + - PORT=7480 + - RAFT_PORT=8000 + - ML_PORT=7946 + - KEY=/generic/ssl/certs/echovault/server1.key + - CERT=/generic/ssl/certs/echovault/server1.crt + - SERVER_ID=1 + - DATA_DIR=/var/lib/echovault + - IN_MEMORY=false + - TLS=false + - MTLS=false + - BOOTSTRAP_CLUSTER=true + - ACL_CONFIG=/generic/config/echovault/acl.yml + - REQUIRE_PASS=false + - FORWARD_COMMAND=true + - SNAPSHOT_THRESHOLD=1000 + - SNAPSHOT_INTERVAL=5m30s + - RESTORE_SNAPSHOT=false + - RESTORE_AOF=false + - AOF_SYNC_STRATEGY=everysec + - MAX_MEMORY=2000kb + - EVICTION_POLICY=allkeys-lfu + # List of server cert/key pairs + - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key + - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key + # List of client certificate authorities + - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt + ports: + - "7481:7480" + - "7945:7946" + - "8000:8000" + volumes: + - ./config/acl.yml:/generic/config/echovault/acl.yml + - ./volumes/cluster_node_1:/var/lib/echovault + networks: + - testnet + + cluster_node_2: + container_name: cluster_node_2 + build: + context: . + dockerfile: Dockerfile.dev + environment: + - PORT=7480 + - RAFT_PORT=8000 + - ML_PORT=7946 + - KEY=/generic/ssl/certs/echovault/server1.key + - CERT=/generic/ssl/certs/echovault/server1.crt + - SERVER_ID=2 + - JOIN_ADDR=cluster_node_1:7946 + - DATA_DIR=/var/lib/echovault + - IN_MEMORY=false + - TLS=false + - MTLS=false + - BOOTSTRAP_CLUSTER=false + - ACL_CONFIG=/generic/config/echovault/acl.yml + - REQUIRE_PASS=false + - FORWARD_COMMAND=true + - SNAPSHOT_THRESHOLD=1000 + - SNAPSHOT_INTERVAL=5m30s + - RESTORE_SNAPSHOT=false + - RESTORE_AOF=false + - AOF_SYNC_STRATEGY=everysec + - MAX_MEMORY=2000kb + - EVICTION_POLICY=allkeys-lfu + # List of server cert/key pairs + - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key + - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key + # List of client certificate authorities + - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt + ports: + - "7482:7480" + - "7947:7946" + - "8001:8000" + volumes: + - ./config/acl.yml:/generic/config/echovault/acl.yml + - ./volumes/cluster_node_2:/var/lib/echovault + networks: + - testnet + + cluster_node_3: + container_name: cluster_node_3 + build: + context: . + dockerfile: Dockerfile.dev + environment: + - PORT=7480 + - RAFT_PORT=8000 + - ML_PORT=7946 + - KEY=/generic/ssl/certs/echovault/server1.key + - CERT=/generic/ssl/certs/echovault/server1.crt + - SERVER_ID=3 + - JOIN_ADDR=cluster_node_1:7946 + - DATA_DIR=/var/lib/echovault + - IN_MEMORY=false + - TLS=false + - MTLS=false + - BOOTSTRAP_CLUSTER=false + - ACL_CONFIG=/generic/config/echovault/acl.yml + - REQUIRE_PASS=false + - FORWARD_COMMAND=true + - SNAPSHOT_THRESHOLD=1000 + - SNAPSHOT_INTERVAL=5m30s + - RESTORE_SNAPSHOT=false + - RESTORE_AOF=false + - AOF_SYNC_STRATEGY=everysec + - MAX_MEMORY=2000kb + - EVICTION_POLICY=allkeys-lfu + # List of server cert/key pairs + - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key + - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key + # List of client certificate authorities + - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt + ports: + - "7483:7480" + - "7948:7946" + - "8002:8000" + volumes: + - ./config/acl.yml:/generic/config/echovault/acl.yml + - ./volumes/cluster_node_3:/var/lib/echovault + networks: + - testnet + + cluster_node_4: + container_name: cluster_node_4 + build: + context: . + dockerfile: Dockerfile.dev + environment: + - PORT=7480 + - RAFT_PORT=8000 + - ML_PORT=7946 + - KEY=/generic/ssl/certs/echovault/server1.key + - CERT=/generic/ssl/certs/echovault/server1.crt + - SERVER_ID=4 + - JOIN_ADDR=cluster_node_1:7946 + - DATA_DIR=/var/lib/echovault + - IN_MEMORY=false + - TLS=false + - MTLS=false + - BOOTSTRAP_CLUSTER=false + - ACL_CONFIG=/generic/config/echovault/acl.yml + - REQUIRE_PASS=false + - FORWARD_COMMAND=true + - SNAPSHOT_THRESHOLD=1000 + - SNAPSHOT_INTERVAL=5m30s + - RESTORE_SNAPSHOT=false + - RESTORE_AOF=false + - AOF_SYNC_STRATEGY=everysec + - MAX_MEMORY=2000kb + - EVICTION_POLICY=allkeys-lfu + # List of server cert/key pairs + - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key + - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key + # List of client certificate authorities + - CLIENT_CA_1=/generic/ssl/certs/echovault/client/rootCA.crt + ports: + - "7484:7480" + - "7949:7946" + - "8003:8000" + volumes: + - ./config/acl.yml:/generic/config/echovault/acl.yml + - ./volumes/cluster_node_4:/var/lib/echovault + networks: + - testnet + + cluster_node_5: + container_name: cluster_node_5 + build: + context: . + dockerfile: Dockerfile.dev + environment: + - PORT=7480 + - RAFT_PORT=8000 + - ML_PORT=7946 + - KEY=/generic/ssl/certs/echovault/server1.key + - CERT=/generic/ssl/certs/echovault/server1.crt + - SERVER_ID=5 + - JOIN_ADDR=cluster_node_1:7946 + - DATA_DIR=/var/lib/echovault + - IN_MEMORY=false + - TLS=false + - MTLS=false + - BOOTSTRAP_CLUSTER=false + - ACL_CONFIG=/generic/config/echovault/acl.yml + - REQUIRE_PASS=false + - FORWARD_COMMAND=true + - SNAPSHOT_THRESHOLD=1000 + - SNAPSHOT_INTERVAL=5m30s + - RESTORE_SNAPSHOT=false + - RESTORE_AOF=false + - AOF_SYNC_STRATEGY=everysec + - MAX_MEMORY=2000kb + - EVICTION_POLICY=allkeys-lfu + # List of server cert/key pairs + - CERT_KEY_PAIR_1=/generic/ssl/certs/echovault/server/server1.crt,/generic/ssl/certs/echovault/server/server1.key + - CERT_KEY_PAIR_2=/generic/ssl/certs/echovault/server/server2.crt,/generic/ssl/certs/echovault/server/server2.key + # 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 \ No newline at end of file diff --git a/src/modules/generic/commands.go b/src/modules/generic/commands.go index 9c40d46..91218a1 100644 --- a/src/modules/generic/commands.go +++ b/src/modules/generic/commands.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/echovault/echovault/src/utils" + "log" "net" "strings" "time" @@ -206,6 +207,23 @@ func handleMGet(ctx context.Context, cmd []string, server utils.Server, _ *net.C 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 { return []utils.Command{ { @@ -249,5 +267,13 @@ PXAT - Expire at the exat time in unix milliseconds (positive integer).`, KeyExtractionFunc: mgetKeyFunc, 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, + }, } } diff --git a/src/modules/generic/key_funcs.go b/src/modules/generic/key_funcs.go index ee194d6..21cfc6e 100644 --- a/src/modules/generic/key_funcs.go +++ b/src/modules/generic/key_funcs.go @@ -38,3 +38,10 @@ func mgetKeyFunc(cmd []string) ([]string, error) { } return cmd[1:], nil } + +func delKeyFunc(cmd []string) ([]string, error) { + if len(cmd) < 2 { + return nil, errors.New(utils.WrongArgsResponse) + } + return cmd[1:], nil +} diff --git a/src/server/keyspace.go b/src/server/keyspace.go index c0426ea..f3696c5 100644 --- a/src/server/keyspace.go +++ b/src/server/keyspace.go @@ -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 // depending on whether an LFU or LRU strategy was used. 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 server.Config.MaxMemory == 0 { return nil diff --git a/src/server/modules.go b/src/server/modules.go index 04db8fe..09e018f 100644 --- a/src/server/modules.go +++ b/src/server/modules.go @@ -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) { for { if !server.StateCopyInProgress.Load() { diff --git a/src/server/server.go b/src/server/server.go index de7b7fa..a6c9dea 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -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 } @@ -319,7 +299,13 @@ func (server *Server) Start(ctx context.Context) { // Initialise raft and memberlist server.raft.RaftInit(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 if conf.RestoreAOF { err := server.AOFEngine.Restore() @@ -406,3 +392,24 @@ func (server *Server) ShutDown(ctx context.Context) { 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 +}