Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More refactoring for multi-shard commands #1226

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
033d258
removed unwanted goroutine
Sep 27, 2024
9e38ea6
Merge branch 'DiceDB:master' into master
AshwinKul28 Sep 29, 2024
a4bf6ca
Merge branch 'DiceDB:master' into master
AshwinKul28 Sep 30, 2024
8ebf0d8
Merge branch 'DiceDB:master' into master
AshwinKul28 Sep 30, 2024
ee4ec24
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 2, 2024
d192f5e
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 3, 2024
8c84231
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 4, 2024
c93645a
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 7, 2024
3a9a641
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 9, 2024
95762b4
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 10, 2024
d683100
made errors lowercase to match with the existing scenario of redis
AshwinKul28 Oct 11, 2024
f3ee04d
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 14, 2024
3c0970c
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 18, 2024
2a388d9
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 21, 2024
f0f7603
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 22, 2024
c232bb9
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 23, 2024
ac4f556
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 24, 2024
48fbe7b
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 27, 2024
94c3579
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 29, 2024
2652a6d
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 30, 2024
fceddcf
Merge branch 'DiceDB:master' into master
AshwinKul28 Oct 31, 2024
3a6c1d9
migrate cmds multishard
AshwinKul28 Oct 31, 2024
e95a921
Update COPY.md
AshwinKul28 Oct 31, 2024
c24abe1
more refactoring
AshwinKul28 Oct 31, 2024
a01d8c1
fix for http
AshwinKul28 Oct 31, 2024
c4dee88
lint fix
AshwinKul28 Oct 31, 2024
0bbc37b
fixed decompose errors
AshwinKul28 Oct 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions docs/src/content/docs/commands/COPY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ COPY <source> <destination> [DB destination-db] [REPLACE]
```

## Parameters
|Parameter | Description | Type | Required|

|Parameter | Description | Type | Required|
|----------|-------------|------|:---------:|
| source |The key of the value you want to copy. This key must exist. | String | Yes|
| destination | The key where the value will be copied to. This key must not exist unless the `REPLACE` option is specified. | String | Yes |
Expand All @@ -21,10 +22,11 @@ COPY <source> <destination> [DB destination-db] [REPLACE]


## Return Value

| Condition | Return Value |
|-----------| :-------------:|
| key was copied successfully | `1` |
| key was not copied | `0` |
| key was not copied | `0` |


## Behaviour
Expand All @@ -41,10 +43,10 @@ When the `COPY` command is executed, DiceDB will:

The `COPY` command can raise the following errors:

1. `Wrong type of value or key`:
1. `Wrong type of value or key`:
- Error message `(error) WRONGTYPE Operation against a key holding the wrong kind of value`:
- This error occurs if the source key holds a value that is not compatible with the `COPY` operation.
2. `Not existent key`:
2. `Not existent key`:
- Error message: `(error) ERR no such key`
- This error occurs if the source key does not exist.
- This error occurs if the destination key already exists and the `REPLACE` option is not specified.
Expand Down Expand Up @@ -78,18 +80,19 @@ Copy the value from `key1` to `key2`, replacing `key2` if it already exists.
```

### Destination key already exist

```bash
127.0.0.1:7379> COPY key1 key2
(integer) 1
127.0.0.1:7379> COPY key1 key2
(integer) 0

```

## Notes

- The `COPY` command is available starting from DiceDB version 6.2.
- The `COPY` command is atomic, meaning that the copy operation is performed as a single, indivisible operation.
- The `COPY` command does not modify the source key; it only duplicates its value to the destination key.
- The `DB destinationDB` is not yet supported with the current versions of DiceDB. It acts as a placeholder for now. It will still return `0`
By understanding the `COPY` command and its parameters, you can effectively duplicate keys within your DiceDB databases, ensuring data consistency and reducing the need for manual data manipulation.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package async
package resp

import (
"testing"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package async
package resp

import (
"testing"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package async
package resp

import (
"testing"
Expand Down
27 changes: 26 additions & 1 deletion internal/cmd/cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,36 @@ import (
"strings"

"github.com/dgryski/go-farm"
"github.com/dicedb/dice/internal/object"
)

// DiceDBCmd represents a command structure to be executed
// within a DiceDB system. This struct emulates the way DiceDB commands
// are structured, including the command itself, additional arguments,
// and an optional object to store or manipulate.
type DiceDBCmd struct {
Cmd string
// Cmd represents the command to execute (e.g., "SET", "GET", "DEL").
// This is the main command keyword that specifies the action to perform
// in DiceDB. For example:
// - "SET": To store a value.
// - "GET": To retrieve a value.
// - "DEL": To delete a value.
// - "EXPIRE": To set a time-to-live for a key.
Cmd string

// Args holds any additional parameters required by the command.
// For example:
// - If Cmd is "SET", Args might contain ["key", "value"].
// - If Cmd is "EXPIRE", Args might contain ["key", "seconds"].
// This slice allows flexible support for commands with variable arguments.
Args []string

// Obj is a pointer to an ExtendedObj, representing an optional data structure
// associated with the command. This contains pointer to the underlying simple
// types such as int, string or even complex types
// like hashes, sets, or sorted sets, which are stored and manipulated as objects.
// WARN: This parameter should be used with caution
Obj *object.ExtendedObj
}

type RedisCmds struct {
Expand Down
8 changes: 8 additions & 0 deletions internal/errors/migrated_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,11 @@ var (
return fmt.Errorf("ERR wrong type of path value - expected %s but found %s", expectedType, actualType) // Signals an unexpected type received when an integer was expected.
}
)

type PreProcessError struct {
Result interface{}
}

func (e *PreProcessError) Error() string {
return fmt.Sprintf("%v", e.Result)
}
24 changes: 23 additions & 1 deletion internal/eval/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package eval
import (
"strings"

"github.com/dicedb/dice/internal/cmd"
dstore "github.com/dicedb/dice/internal/store"
)

Expand All @@ -29,6 +30,8 @@ type DiceCmdMeta struct {
// will utilize this function for evaluation, allowing for better handling of
// complex command execution scenarios and improved response consistency.
NewEval func([]string, *dstore.Store) *EvalResponse

StoreObjectEval func(*cmd.DiceDBCmd, *dstore.Store) *EvalResponse
}

type KeySpecs struct {
Expand All @@ -38,8 +41,11 @@ type KeySpecs struct {
}

var (
DiceCmds = map[string]DiceCmdMeta{}
PreProcessing = map[string]func([]string, *dstore.Store) *EvalResponse{}
DiceCmds = map[string]DiceCmdMeta{}
)

var (
echoCmdMeta = DiceCmdMeta{
Name: "ECHO",
Info: `ECHO returns the string given as argument.`,
Expand Down Expand Up @@ -603,12 +609,24 @@ var (
Info: "PERSIST removes the expiration from a key",
Eval: evalPersist,
}

//TODO: supports only http protocol, needs to be removed once http is migrated to multishard
copyCmdMeta = DiceCmdMeta{
Name: "COPY",
Info: `COPY command copies the value stored at the source key to the destination key.`,
Eval: evalCOPY,
Arity: -2,
}

//TODO: supports only http protocol, needs to be removed once http is migrated to multishard
objectCopyCmdMeta = DiceCmdMeta{
Name: "OBJECTCOPY",
Info: `COPY command copies the value stored at the source key to the destination key.`,
StoreObjectEval: evalCOPYObject,
IsMigrated: true,
Arity: -2,
}

decrCmdMeta = DiceCmdMeta{
Name: "DECR",
Info: `DECR decrements the value of the specified key in args by 1,
Expand Down Expand Up @@ -1299,6 +1317,9 @@ var (
)

func init() {
PreProcessing["COPY"] = evalGetObject
PreProcessing["RENAME"] = evalGET

DiceCmds["ABORT"] = abortCmdMeta
DiceCmds["APPEND"] = appendCmdMeta
DiceCmds["AUTH"] = authCmdMeta
Expand All @@ -1322,6 +1343,7 @@ func init() {
DiceCmds["COMMAND|DOCS"] = commandDocsCmdMeta
DiceCmds["COMMAND|GETKEYSANDFLAGS"] = commandGetKeysAndFlagsCmdMeta
DiceCmds["COPY"] = copyCmdMeta
DiceCmds["OBJECTCOPY"] = objectCopyCmdMeta
DiceCmds["DBSIZE"] = dbSizeCmdMeta
DiceCmds["DECR"] = decrCmdMeta
DiceCmds["DECRBY"] = decrByCmdMeta
Expand Down
2 changes: 1 addition & 1 deletion internal/eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -2157,7 +2157,7 @@ func evalCOPY(args []string, store *dstore.Store) []byte {

for i := 2; i < len(args); i++ {
arg := strings.ToUpper(args[i])
if arg == "REPLACE" {
if arg == dstore.Replace {
isReplace = true
}
}
Expand Down
48 changes: 40 additions & 8 deletions internal/eval/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,50 @@ import (
dstore "github.com/dicedb/dice/internal/store"
)

func ExecuteCommand(c *cmd.DiceDBCmd, client *comm.Client, store *dstore.Store, httpOp, websocketOp bool) *EvalResponse {
diceCmd, ok := DiceCmds[c.Cmd]
type Eval struct {
cmd *cmd.DiceDBCmd
client *comm.Client
store *dstore.Store
isHTTPOperation bool
isWebSocketOperation bool
isPreprocessOperation bool
}

func NewEval(c *cmd.DiceDBCmd, client *comm.Client, store *dstore.Store, httpOp, websocketOp, preProcessing bool) *Eval {
return &Eval{
cmd: c,
client: client,
store: store,
isHTTPOperation: httpOp,
isWebSocketOperation: websocketOp,
isPreprocessOperation: preProcessing,
}
}

func (e *Eval) PreProcessCommand() *EvalResponse {
if f, ok := PreProcessing[e.cmd.Cmd]; ok {
return f(e.cmd.Args, e.store)
}
return &EvalResponse{Result: nil, Error: diceerrors.ErrInternalServer}
}

func (e *Eval) ExecuteCommand() *EvalResponse {
diceCmd, ok := DiceCmds[e.cmd.Cmd]
if !ok {
return &EvalResponse{Result: diceerrors.NewErrWithFormattedMessage("unknown command '%s', with args beginning with: %s", c.Cmd, strings.Join(c.Args, " ")), Error: nil}
return &EvalResponse{Result: diceerrors.NewErrWithFormattedMessage("unknown command '%s', with args beginning with: %s", e.cmd.Cmd, strings.Join(e.cmd.Args, " ")), Error: nil}
}

// Temporary logic till we move all commands to new eval logic.
// MigratedDiceCmds map contains refactored eval commands
// For any command we will first check in the existing map
// if command is NA then we will check in the new map

if diceCmd.IsMigrated {
return diceCmd.NewEval(c.Args, store)
if e.cmd.Obj != nil {
return diceCmd.StoreObjectEval(e.cmd, e.store)
}

return diceCmd.NewEval(e.cmd.Args, e.store)
}

// The following commands could be handled at the shard level, however, we can randomly let any shard handle them
Expand All @@ -31,14 +63,14 @@ func ExecuteCommand(c *cmd.DiceDBCmd, client *comm.Client, store *dstore.Store,
// Old implementation kept as it is, but we will be moving
// to the new implementation soon for all commands
case "SUBSCRIBE", "Q.WATCH":
return &EvalResponse{Result: EvalQWATCH(c.Args, httpOp, websocketOp, client, store), Error: nil}
return &EvalResponse{Result: EvalQWATCH(e.cmd.Args, e.isHTTPOperation, e.isWebSocketOperation, e.client, e.store), Error: nil}
case "UNSUBSCRIBE", "Q.UNWATCH":
return &EvalResponse{Result: EvalQUNWATCH(c.Args, httpOp, client), Error: nil}
return &EvalResponse{Result: EvalQUNWATCH(e.cmd.Args, e.isHTTPOperation, e.client), Error: nil}
case auth.Cmd:
return &EvalResponse{Result: EvalAUTH(c.Args, client), Error: nil}
return &EvalResponse{Result: EvalAUTH(e.cmd.Args, e.client), Error: nil}
case "ABORT":
return &EvalResponse{Result: clientio.RespOK, Error: nil}
default:
return &EvalResponse{Result: diceCmd.Eval(c.Args, store), Error: nil}
return &EvalResponse{Result: diceCmd.Eval(e.cmd.Args, e.store), Error: nil}
}
}
Loading