diff --git a/config/config.go b/config/config.go index 83c7f25b6..8c85f5fea 100644 --- a/config/config.go +++ b/config/config.go @@ -65,13 +65,13 @@ type Config struct { EvictionRatio float64 `mapstructure:"evictionratio"` KeysLimit int `mapstructure:"keyslimit"` AOFFile string `mapstructure:"aoffile"` - PersistenceEnabled bool `mapstructure:"persistenceenabled"` WriteAOFOnCleanup bool `mapstructure:"writeaofoncleanup"` LFULogFactor int `mapstructure:"lfulogfactor"` LogLevel string `mapstructure:"loglevel"` PrettyPrintLogs bool `mapstructure:"prettyprintlogs"` EnableMultiThreading bool `mapstructure:"enablemultithreading"` StoreMapInitSize int `mapstructure:"storemapinitsize"` + WatchChanBufSize int `mapstructure:"watchchanbufsize"` } `mapstructure:"server"` Auth struct { UserName string `mapstructure:"username"` @@ -99,20 +99,20 @@ var baseConfig = Config{ EvictionRatio float64 `mapstructure:"evictionratio"` KeysLimit int `mapstructure:"keyslimit"` AOFFile string `mapstructure:"aoffile"` - PersistenceEnabled bool `mapstructure:"persistenceenabled"` WriteAOFOnCleanup bool `mapstructure:"writeaofoncleanup"` LFULogFactor int `mapstructure:"lfulogfactor"` LogLevel string `mapstructure:"loglevel"` PrettyPrintLogs bool `mapstructure:"prettyprintlogs"` EnableMultiThreading bool `mapstructure:"enablemultithreading"` StoreMapInitSize int `mapstructure:"storemapinitsize"` + WatchChanBufSize int `mapstructure:"watchchanbufsize"` }{ Addr: DefaultHost, Port: DefaultPort, KeepAlive: int32(300), Timeout: int32(300), MaxConn: int32(0), - ShardCronFrequency: 1 * time.Second, + ShardCronFrequency: 30 * time.Second, MultiplexerPollTimeout: 100 * time.Millisecond, MaxClients: int32(20000), MaxMemory: 0, @@ -120,13 +120,13 @@ var baseConfig = Config{ EvictionRatio: 0.9, KeysLimit: DefaultKeysLimit, AOFFile: "./dice-master.aof", - PersistenceEnabled: true, WriteAOFOnCleanup: false, LFULogFactor: 10, LogLevel: "info", PrettyPrintLogs: false, EnableMultiThreading: false, StoreMapInitSize: 1024000, + WatchChanBufSize: 20000, }, Auth: struct { UserName string `mapstructure:"username"` @@ -305,7 +305,14 @@ func mergeFlagsWithConfig() { // This function checks if the config file is present or not at ConfigFileLocation func isConfigFilePresent() bool { + // If config file present in current directory use it + if _, err := os.Stat(filepath.Join(".", DefaultConfigName)); err == nil { + FileLocation = filepath.Join(".", DefaultConfigName) + return true + } + _, err := os.Stat(FileLocation) + return err == nil } diff --git a/dice.toml b/dice.toml new file mode 100644 index 000000000..3bd1f2c66 --- /dev/null +++ b/dice.toml @@ -0,0 +1,31 @@ +[Server] +Addr = '0.0.0.0' +Port = 7379 +KeepAlive = 300 +Timeout = 300 +MaxConn = 0 +# Value in nanoseconds (30 seconds) +ShardCronFrequency = 30000000000 +# Value in nanoseconds (100 ms/0.1 seconds) +MultiplexerPollTimeout = 100000000 +MaxClients = 20000 +MaxMemory = 0 +EvictionPolicy = 'allkeys-lfu' +EvictionRatio = 0.4 +KeysLimit = 100000 +AOFFile = './dice-master.aof' +WriteAOFOnCleanup = false +LFULogFactor = 10 +LogLevel = 'info' +PrettyPrintLogs = false +EnableMultiThreading = false +StoreMapInitSize = 10240 +WatchChanBufSize = 20000 + +[Auth] +UserName = 'dice' +Password = '' + +[Network] +IOBufferLength = 512 +IOBufferLengthMAX = 51200 diff --git a/docs/src/content/docs/commands/AUTH.md b/docs/src/content/docs/commands/AUTH.md index 38dadb659..c2867b735 100644 --- a/docs/src/content/docs/commands/AUTH.md +++ b/docs/src/content/docs/commands/AUTH.md @@ -13,12 +13,16 @@ AUTH password ## Parameters -- `password` (string): The password that you have set for the DiceDB server. This is a required parameter for the `AUTH` command. If the password is correct, the command will return a success response; if it is incorrect, an error will be raised. +| Parameter | Description | Type | Required | +|-----------|---------------------------------------------------------------------------|---------|----------| +| `password`| The password that you have set for the DiceDB server. | String | Yes | -## Return Value +## Return values -- On success: The command returns the string `OK`, indicating that the authentication was successful. -- On failure: If the authentication fails (due to an incorrect password), it raises an error indicating the failure reason. +| Condition | Return Value | +|------------------------------------------------|---------------------------------------------------| +| Authentication is successful | `OK` | +| Syntax or specified constraints are invalid | error | ## Behaviour @@ -32,42 +36,58 @@ When the `AUTH` command is executed, DiceDB will perform the following steps: Once authenticated, the client can execute commands on the DiceDB server that require authentication. If a client attempts to send commands without being authenticated (and while authentication is required), DiceDB will respond with an error for unauthorized access. +## Errors + +1. `(error) WRONGPASS invalid username-password pair or user is disabled`: This error occurs if the password provided to the `AUTH` command does not match the configured password of the DiceDB server. The client will not be granted access unless the correct password is provided. + +2. `(error) NOAUTH Authentication required`: This error occurs when a client attempts to execute a command without authenticating first. The client must use the `AUTH` command to authenticate before executing any other commands. + +3. `(error) ERR AUTH called without any password configured for the default user. Are you sure your configuration is correct?`: This error will be raised if the DiceDB server is not configured to use a password (i.e., the `requirepass` directive in the DiceDB.conf file is empty or commented out), and a client attempts to authenticate using the `AUTH` command. + ## Example Usage ### Successful Authentication +In this example, after successfully typing the correct password, the DiceDB server responds with `OK`, indicating that the client is now authenticated and can execute further commands. + ```bash 127.0.0.1:7379> AUTH your_secret_password OK ``` -In this example, after successfully typing the correct password, the DiceDB server responds with `OK`, indicating that the client is now authenticated and can execute further commands. - ### Failed Authentication +In this example, the provided password is incorrect. As a result, the DiceDB server responds with an error indicating that the authentication has failed. + ```bash 127.0.0.1:7379> AUTH incorrect_password (error) WRONGPASS invalid username-password pair or user is disabled ``` -In this example, the provided password is incorrect. As a result, the DiceDB server responds with an error stating `(error) ERR invalid password`, indicating that the authentication has failed. - ### Invoking commands without AUTH +In this example, when some other command is fired without doing `AUTH` then the above error is thrown denoting that authentication is required. + ```bash 127.0.0.1:7379> GET x (error) NOAUTH Authentication required ``` -In this example, when some other command is fired without doing `AUTH` then the above error is thrown denoting that authentication is required. +### Invoking AUTH without configuring password -## Error Handling +In this example, when `AUTH` is fired without actually configuring the password for the DiceDB server, then the server returns an error indicating that no password is set or the configuration is incorrect. -The `AUTH` command may raise the following errors: +```bash +127.0.0.1:7379> AUTH incorrect_password +(error) ERR AUTH called without any password configured for the default user. Are you sure your configuration is correct? +``` -- `(error) ERR invalid password`: This error occurs if the password provided to the `AUTH` command does not match the configured password of the DiceDB server. The client will not be granted access unless the correct password is provided. +### Invalid usage -- `(error) ERR Client sent AUTH but no password is set`: This error will be raised if the DiceDB server is not configured to use a password (i.e., the `requirepass` directive in the DiceDB.conf file is empty or commented out), and a client attempts to authenticate using the `AUTH` command. +```bash +127.0.0.1:7379> AUTH your_secret_password foo bar +(error) ERR wrong number of arguments for 'auth' command +``` ### Additional Notes diff --git a/docs/src/content/docs/commands/BGREWRITEAOF.md b/docs/src/content/docs/commands/BGREWRITEAOF.md index 06c20cb13..2e93a608c 100644 --- a/docs/src/content/docs/commands/BGREWRITEAOF.md +++ b/docs/src/content/docs/commands/BGREWRITEAOF.md @@ -5,21 +5,18 @@ description: The `BGREWRITEAOF` command in DiceDB is used to asynchronously rewr The `BGREWRITEAOF` command in DiceDB is used to asynchronously rewrite the Append-Only File (AOF). This command triggers a background process that creates a new AOF file, which is a more compact and optimized version of the current AOF file. The new AOF file will contain the minimal set of commands needed to reconstruct the current dataset. -## Parameters +## Syntax -The `BGREWRITEAOF` command does not take any parameters. +``` +BGREWRITEAOF +``` -## Return Value +## Return values -- `Simple String Reply`: The command returns a simple string reply indicating the status of the operation. - - If the background rewrite operation is successfully started, the reply will be: - ``` - "Background append only file rewriting started" - ``` - - If the operation cannot be started because a previous `BGREWRITEAOF` operation is still in progress, the reply will be: - ``` - "Background append only file rewriting already in progress" - ``` +| Condition | Return Value | +|------------------------------------------------|---------------------------------------------------| +| Command is successful | `OK` | +| Syntax or specified constraints are invalid | error | ## Behaviour @@ -30,45 +27,22 @@ When the `BGREWRITEAOF` command is issued, DiceDB performs the following steps: 3. `Swapping Files`: Once the temporary AOF file is fully written and synced to disk, the child process swaps the temporary file with the existing AOF file. 4. `Cleaning Up`: The child process exits, and the main DiceDB server continues to operate with the new, optimized AOF file. -## Example Usage - -```sh -127.0.0.1:7379> BGREWRITEAOF -"Background append only file rewriting started" -``` - -In this example, the `BGREWRITEAOF` command is issued, and the server responds with a confirmation that the background rewrite process has started. +## Errors -## Error Handling +1. `Unable to create/write AOF file`: -### Errors - -1. `Background Rewrite Already in Progress`: - - - `Condition`: If a `BGREWRITEAOF` operation is already in progress when the command is issued again. - - `Error Message`: - ``` - "Background append only file rewriting already in progress" - ``` + - Error Message: `ERR AOF failed` + - Occurs when diceDB is unable to create or write into the AOF file 2. `Forking Error`: - - `Condition`: If DiceDB is unable to fork a new process due to system limitations or resource constraints. - - `Error Message`: - ``` - "ERR Can't fork" - ``` - -3. `AOF Disabled`: - - - `Condition`: If AOF is disabled in the DiceDB configuration. - - `Error Message`: - ``` - "ERR AOF is not enabled" - ``` + - Error Message: `ERR Fork failed` + - Occurs when diceDB is unable to fork a new process due to system limitations or resource constraints. -## Best Practices +## Example Usage -- `Regular Maintenance`: Schedule regular `BGREWRITEAOF` operations during off-peak hours to ensure the AOF file remains compact and optimized. -- `Monitor Resource Usage`: Be aware that the `BGREWRITEAOF` operation can be resource-intensive. Monitor CPU and memory usage to avoid potential performance degradation. -- `Backup Before Rewrite`: Consider taking a backup of the current AOF file before initiating a rewrite, especially in production environments. +### Basic Usage +```sh +127.0.0.1:7379> BGREWRITEAOF +OK +``` diff --git a/docs/src/content/docs/commands/DECRBY.md b/docs/src/content/docs/commands/DECRBY.md index d05b0e62b..0bd71a95c 100644 --- a/docs/src/content/docs/commands/DECRBY.md +++ b/docs/src/content/docs/commands/DECRBY.md @@ -1,6 +1,6 @@ --- title: DECRBY -description: Documentation for the DiceDB command DECRBY +description: The `DECRBY` command in DiceDB is used to decrement the integer value of a key by a specified amount. This command is useful for scenarios where you need to decrease a counter or a numeric value stored in a key. --- The `DECRBY` command in DiceDB is used to decrement the integer value of a key by a specified amount. This command is useful for scenarios where you need to decrease a counter or a numeric value stored in a key. @@ -8,100 +8,92 @@ The `DECRBY` command in DiceDB is used to decrement the integer value of a key b ## Syntax ``` -DECRBY key decrement +DECRBY key delta ``` ## Parameters -- `key`: The key whose value you want to decrement. This key must hold a string that can be represented as an integer. -- `decrement`: The integer value by which the key's value should be decreased. This value can be positive or negative. +| Parameter | Description | Type | Required | +|-----------|---------------------------------------------------------------------------------------------------------------|---------|----------| +| `key` | The key whose value you want to decrement. This key must hold a string that can be represented as an integer. | String | Yes | +|`delta` | The integer value by which the key's value should be decreased. This value can be positive or negative. | String | Yes | -## Return Value -The command returns the value of the key after the decrement operation has been performed. +## Return values + +| Condition | Return Value | +|--------------------------------------------------|------------------------------------------------------------------| +| Key exists and holds an integer string | `(integer)` The value of the key after decrementing by delta. | +| Key does not exist | `(integer)` -delta | -## Behaviour +## Behaviour When the `DECRBY` command is executed, the following steps occur: -1. DiceDB checks if the key exists. -2. If the key does not exist, DiceDB treats the key's value as 0 before performing the decrement operation. -3. If the key exists but does not hold a string that can be represented as an integer, an error is returned. -4. The value of the key is decremented by the specified decrement value. -5. The new value of the key is returned. +- DiceDB checks if the key exists. +- If the key does not exist, DiceDB treats the key's value as 0 before performing the decrement operation. +- If the key exists but does not hold a string that can be represented as an integer, an error is returned. +- The value of the key is decremented by the specified decrement value. +- The new value of the key is returned. +## Errors -## Error Handling +The `DECRBY` command can raise errors in the following scenarios: -The `DECRBY` command can raise the following errors: +1. `Wrong Type Error`: -- `WRONGTYPE Operation against a key holding the wrong kind of value`: This error occurs if the key exists but its value is not a string that can be represented as an integer. -- `ERR value is not an integer or out of range`: This error occurs if the decrement value provided is not a valid integer. + - Error Message: `ERROR value is not an integer or out of range` + - This error occurs if the decrement value provided is not a valid integer. + - This error occurs if the key exists but its value is not a string that can be represented as an integer -## Example Usage +2. `Syntax Error`: -### Example 1: Basic Decrement + - Error Message: `ERROR wrong number of arguments for 'decrby' command` + - Occurs if the command is called without the required parameter. -```DiceDB -SET mycounter 10 -DECRBY mycounter 3 -``` -`Output:` +## Examples -``` -(integer) 7 -``` +### Example with Decrementing the Value of an Existing Key -In this example, the value of `mycounter` is decremented by 3, resulting in a new value of 7. -### Example 2: Decrementing a Non-Existent Key - -```DiceDB -DECRBY newcounter 5 -``` - -`Output:` - -``` -(integer) -5 +```bash +127.0.0.1:7379>SET mycounter 10 +OK +127.0.0.1:7379>DECRBY mycounter 3 +(integer)7 ``` +`Explanation:` -In this example, since `newcounter` does not exist, DiceDB treats its value as 0 and decrements it by 5, resulting in a new value of -5. +- In this example, the value of `mycounter` is set to 10 +- The `DECRBY` command decremented `mycounter`by 3, resulting in a new value of 7. -### Example 3: Error Handling - Non-Integer Value +### Example with Decrementing a Non-Existent Key (Implicit Initialization to 0) -```DiceDB -SET mystring "hello" -DECRBY mystring 2 +```bash +127.0.0.1:7379>DECRBY newcounter 5 +(integer)-5 ``` - -`Output:` - -``` -(error) WRONGTYPE Operation against a key holding the wrong kind of value -``` - -In this example, the key `mystring` holds a non-integer value, so the `DECRBY` command returns an error. - -### Example 4: Error Handling - Invalid Decrement Value - -```DiceDB -DECRBY mycounter "two" +`Explanation:` +- In this example, since `newcounter` does not exist, DiceDB treats its value as 0 and decrements it by 5, resulting in a new value of -5. +### Example with Error Due to Non-Integer Value in Key + +```bash +127.0.0.1:7379>SET mystring "hello" +OK +127.0.0.1:7379>DECRBY mystring 2 +(error) ERROR value is not an integer or out of range ``` +`Explanation:` +- In this example, the key `mystring` holds a non-integer value, so the `DECRBY` command returns an error. -`Output:` +### Example with Error Due to Invalid Decrement Value (Non-Integer Decrement) +```bash +127.0.0.1:7379>DECRBY mycounter "two" +(error) ERROR value is not an integer or out of range ``` -(error) ERR value is not an integer or out of range -``` - -In this example, the decrement value "two" is not a valid integer, so the `DECRBY` command returns an error. - -## Notes -- The `DECRBY` command is atomic, meaning that even if multiple clients issue `DECRBY` commands concurrently, DiceDB ensures that the value is decremented correctly. -- If the key's value is not a valid integer, the command will fail with an error. -- The decrement value can be negative, which effectively makes the `DECRBY` command an increment operation. +`Explanation:` +- In this example, the decrement value "two" is not a valid integer, so the `DECRBY` command returns an error. -By understanding the `DECRBY` command, you can effectively manage and manipulate integer values stored in DiceDB keys, ensuring accurate and efficient data handling in your applications. diff --git a/docs/src/content/docs/commands/DEL.md b/docs/src/content/docs/commands/DEL.md index 9ad03e2cb..525b377cc 100644 --- a/docs/src/content/docs/commands/DEL.md +++ b/docs/src/content/docs/commands/DEL.md @@ -1,35 +1,41 @@ --- title: DEL -description: The `DEL` command in DiceDB is used to remove one or more keys from the database. If a given key does not exist, it is ignored. The command returns the number of keys that were removed. +description: The `DEL` command in DiceDB is used to remove one or more keys from the database. If a given key does not exist, it is ignored. This command is fundamental for data management in DiceDB, allowing for the deletion of key-value pairs. The command returns the number of keys that were removed. + + --- -The `DEL` command in DiceDB is used to remove one or more keys from the database. If a given key does not exist, it is ignored. The command returns the number of keys that were removed. +The `DEL` command in DiceDB is used to remove one or more keys from the database. If a given key does not exist, it is ignored. This command is fundamental for data management in DiceDB, allowing for the deletion of key-value pairs. The command returns the number of keys that were removed. ## Syntax -```plaintext +``` DEL key [key ...] ``` ## Parameters -- `key`: The key(s) to be removed from the database. Multiple keys can be specified, separated by spaces. +| Parameter | Description | Type | Required | +|-----------|--------------------------------------------------|--------|----------| +| `key` | The name of the key(s) to be deleted. | String | Yes | -## Return Value +## Return values -The `DEL` command returns an integer representing the number of keys that were removed. - -- `Type`: Integer -- `Description`: The number of keys that were successfully removed. +| Condition | Return Value | +|-------------------------------------|---------------------------------------------------| +| Command is successful | Integer (number of keys successfully deleted) | +| No keys match the specified pattern | 0 | +| Syntax or specified constraints are invalid | error | ## Behaviour When the `DEL` command is executed, DiceDB will attempt to remove the specified keys from the database. The command operates in the following manner: 1. `Key Existence Check`: For each key specified, DiceDB checks if the key exists in the database. -2. `Key Removal`: If a key exists, it is removed from the database. +2. `Key Removal`: If a key exists, it is removed from the database along with its associated value, regardless of the value's type. 3. `Count Removal`: The command keeps a count of how many keys were successfully removed. -4. `Return Count`: The total count of removed keys is returned as the result of the command. +4. `Ignore Non-existent Keys`: If a specified key does not exist, it is simply ignored and does not affect the count of removed keys. +5. `Return Count`: The total count of removed keys is returned as the result of the command. ## Error Handling @@ -43,37 +49,67 @@ The `DEL` command is generally robust and straightforward, but there are a few s - `Error Message`: `(error) ERR wrong number of arguments for 'del' command` + ## Example Usage -### Single Key Deletion +### Basic Usage + +Deleting a single key `foo`: + +```bash +127.0.0.1:7379> DEL foo +(integer) 1 +``` + +### Deleting Multiple Keys + +Deleting multiple keys `foo`, `bar`, and `baz`: ```bash -127.0.0.1:7379> DEL mykey +127.0.0.1:7379> DEL foo bar baz +(integer) 2 ``` -`Description`: This command will attempt to delete the key `mykey` from the database. If `mykey` exists, it will be removed, and the command will return `1`. If `mykey` does not exist, the command will return `0`. +In this example, if only `foo` and `bar` existed, the command would return 2, indicating that two keys were successfully deleted. + +### Deleting Non-existent Keys -### Multiple Keys Deletion +Attempting to delete a non-existent key: ```bash -127.0.0.1:7379> DEL key1 key2 key3 +127.0.0.1:7379> DEL nonexistentkey +(integer) 0 ``` -`Description`: This command will attempt to delete the keys `key1`, `key2`, and `key3` from the database. The return value will be the number of keys that were successfully removed. For example, if `key1` and `key3` exist but `key2` does not, the command will return `2`. +### Complex Example -### Example with Return Values +Setting multiple keys and then deleting them: ```bash 127.0.0.1:7379> SET key1 "value1" +OK 127.0.0.1:7379> SET key2 "value2" +OK 127.0.0.1:7379> SET key3 "value3" +OK 127.0.0.1:7379> DEL key1 key2 key4 +(integer) 2 ``` -`Description`: In this example: - -- `key1`, `key2`, and `key3` are set with some values. +In this example: +- Three keys are set: `key1`, `key2`, and `key3`. - The `DEL` command attempts to delete `key1`, `key2`, and `key4`. -- Since `key1` and `key2` exist, they will be removed. -- `key4` does not exist, so it will be ignored. -- The command will return `2`, indicating that two keys were removed. +- `key1` and `key2` are successfully deleted. +- `key4` doesn't exist, so it's ignored. +- The command returns 2, indicating two keys were deleted. + +### Error Example + +Calling `DEL` without any arguments: + +```bash +127.0.0.1:7379> DEL +(error) ERR wrong number of arguments for 'del' command +``` + + diff --git a/docs/src/content/docs/commands/GETBIT.md b/docs/src/content/docs/commands/GETBIT.md index 78f5a3e06..c59057f55 100644 --- a/docs/src/content/docs/commands/GETBIT.md +++ b/docs/src/content/docs/commands/GETBIT.md @@ -1,6 +1,6 @@ --- title: GETBIT -description: Documentation for the DiceDB command GETBIT +description: The `GETBIT` command is used to retrieve the bit value at a specified offset in the string value stored at a given key. This command is particularly useful for bitwise operations and managing binary data within DiceDB. --- The `GETBIT` command is used to retrieve the bit value at a specified offset in the string value stored at a given key. This command is particularly useful for bitwise operations and managing binary data within DiceDB. @@ -13,61 +13,97 @@ GETBIT key offset ## Parameters -- `key`: The key of the string from which the bit value is to be retrieved. This key must reference a string value. -- `offset`: The position of the bit to retrieve. The offset is a zero-based integer, meaning the first bit is at position 0. +| Parameter | Description | Type | Required | +|-----------|---------------------------------------------------------------------------|---------|----------| +| `key` | The key of the string from which the bit value is to be retrieved. This key must reference a string value. | String | Yes | +| `offset` | The position of the bit to retrieve. The offset is a zero-based integer, meaning the first bit is at position 0. | Integer | Yes | -## Return Value +## Return Values -- `Integer`: The command returns the bit value at the specified offset, which will be either `0` or `1`. +| Condition | Return Value | +|------------------------------------------------|---------------------------------------------------| +| Command is successful | `0` or `1` | +| Syntax or specified constraints are invalid | error | ## Behaviour -When the `GETBIT` command is executed, DiceDB will: +- Check if the specified key exists. +- If the key does not exist, it is treated as if it contains a string of zero bytes, and the bit at any offset will be `0`. +- If the key exists but does not hold a string value, an error is returned. +- If the key exists and holds a string value, the bit at the specified offset is retrieved and returned. +- If the key exists and holds a string value, however the offset is more than string length, `0` will be returned. -1. Check if the specified key exists. -2. If the key does not exist, it is treated as if it contains a string of zero bytes, and the bit at any offset will be `0`. -3. If the key exists but does not hold a string value, an error is returned. -4. If the key exists and holds a string value, the bit at the specified offset is retrieved and returned. +## Errors -## Error Handling +1. `Wrong number of arguments`: -- `WRONGTYPE Operation against a key holding the wrong kind of value`: This error is returned if the key exists but does not contain a string value. -- `ERR bit offset is not an integer or out of range`: This error is returned if the offset is not a valid integer or is negative. + - Error Message: `(error) wrong number of arguments for 'GETBIT' command` + - Occurs if both key and offset are not provided. + +2. `Non-string value stored against the key`: + + - Error Message: `(error) WRONGTYPE Operation against a key holding the wrong kind of value` + - Occurs if the key exists but does not contain a string value. + +3. `Non-integer or negative value for offset`: + + - Error Message: `(error) ERR bit offset is not an integer or out of range` + - Occurs if the offset is not a valid integer or is negative. ## Example Usage -### Example 1: Retrieving a bit from a string +### Basic Usage + +Setting a key `foo` with the value `a` (ASCII value of `a` is `97`, represented as `01100001` in binary). Then retrieve the bit value at index `1`. + +```bash +127.0.0.1:7379> SET foo "a" +OK +127.0.0.1:7379> GETBIT foo 1 +1 +``` + +### Key does not exist + +Trying to retrieve bit value from a non-existent key. -```shell -SET mykey "a" # ASCII value of 'a' is 97, binary representation is 01100001 -GETBIT mykey 1 # Returns 1, as the second bit of 'a' (01100001) is 1 +```bash +127.0.0.1:7379> GETBIT bar 5 +0 ``` -### Example 2: Retrieving a bit from a non-existent key +### Key holds a non-string value -```shell -GETBIT nonExistentKey 5 # Returns 0, as the key does not exist and is treated as a string of zero bytes +Setting a key `baz` with a list of items and retrieving bit value from the key. + +```bash +127.0.0.1:7379> LPUSH baz "item" +127.0.0.1:7379> GETBIT baz 0 +(error) WRONGTYPE Operation against a key holding the wrong kind of value ``` -### Example 3: Error when key holds a non-string value +### Invalid offset + +Setting a key `foo` with the value `a` and trying to retrieve non-integer and negative bit value. -```shell -LPUSH mylist "item" # Create a list -GETBIT mylist 0 # Returns an error: WRONGTYPE Operation against a key holding the wrong kind of value +```bash +127.0.0.1:7379> SET foo "a" +127.0.0.1:7379> GETBIT foo -1 +(error) ERR bit offset is not an integer or out of range +127.0.0.1:7379> GETBIT foo "abc" +(error) ERR bit offset is not an integer or out of range ``` -### Example 4: Error with invalid offset +### Insufficient parameters -```shell -SET mykey "a" -GETBIT mykey -1 # Returns an error: ERR bit offset is not an integer or out of range -GETBIT mykey "abc" # Returns an error: ERR bit offset is not an integer or out of range +Trying to execute `GETBIT` command without `offset` argument. + +```bash +127.0.0.1:7379> GETBIT foo +(error) wrong number of arguments for 'GETBIT' command ``` ## Notes - The `GETBIT` command operates on the raw binary representation of the string. This means that the offset is counted in bits, not bytes. -- The maximum offset that can be specified is 2^32 - 1 (4294967295), as DiceDB strings are limited to 512 MB. - -By understanding and utilizing the `GETBIT` command, you can efficiently manage and manipulate binary data within your DiceDB database. - +- The maximum offset that can be specified is `2^32 - 1 (4294967295)`, as DiceDB strings are limited to `512 MB`. diff --git a/docs/src/content/docs/commands/HSCAN.md b/docs/src/content/docs/commands/HSCAN.md new file mode 100644 index 000000000..32d08563f --- /dev/null +++ b/docs/src/content/docs/commands/HSCAN.md @@ -0,0 +1,73 @@ +--- +title: HSCAN +description: Documentation for the DiceDB command HSCAN +--- + +The `HSCAN` command is used to incrementally iterate over the fields of a hash stored at a given key. It returns both the next cursor and the matching fields. + +## Syntax + +``` +HSCAN key cursor [MATCH pattern] [COUNT count] +``` + +## Parameters + +- `key`: The key of the hash to scan. +- `cursor`: The cursor indicating the starting position of the scan. +- `MATCH pattern` (optional): Specifies a pattern to match against the fields. Only the fields that match the pattern will be returned. +- `COUNT count` (optional): Specifies the maximum number of fields to return. + +## Return Value + +The `HSCAN` command returns an array containing the next cursor and the matching fields. The format of the returned array is `[nextCursor, [field1, value1, field2, value2, ...]]`. + +## Behaviour +When the `HSCAN` command is executed: + +1. DiceDB checks if the specified key exists. +2. If the key exists and is associated with a hash, DiceDB scans the fields of the hash and returns the next cursor and the matching fields. +3. If the key does not exist, DiceDB returns an empty array. +4. If the key exists but is not associated with a hash, an error is returned. +5. If the key exists and all keys have been scanned, cursor is reset to 0. + +## Error handling +The `HSCAN` command can raise the following errors: + +- `WRONGTYPE Operation against a key holding the wrong kind of value`: This error occurs if the specified key exists but is not associated with a hash. For example, if the key is associated with a string, list, set, or any other data type, this error will be raised. +- `Invalid integer value for COUNT`: This error occurs if the value provided for the `COUNT` option is not a valid integer or is out of range. + + + +## Examples + +```DiceDB +> HSET myhash field1 "value1" field2 "value2" +1) (integer) 2 + +> HSCAN myhash 0 +1) "2" +2) 1) "field1" + 2) "value1" + 3) "field2" + 4) "value2" + +> HSCAN myhash 0 MATCH field* COUNT 1 +1) "1" +2) 1) "field1" + 2) "value1" + +> HSCAN myhash 1 MATCH field* COUNT 1 +1) "0" +2) 1) "field2" + 2) "value2" +``` + + +## Additional Notes + +- The `HSCAN` command has a time complexity of O(N), where N is the number of keys in the hash. This is in contrast to Redis, which implements `HSCAN` in O(1) time complexity by maintaining a cursor. +- The `HSCAN` command is particularly useful for iterating over the fields of a hash in a cursor-based manner, allowing for efficient processing of large hashes. +- The `MATCH` pattern allows for flexible filtering of fields based on their names, making it easy to target specific fields or groups of fields. +- The `COUNT` option enables limiting the number of fields returned, which can be beneficial for performance and memory usage considerations. +- The cursor returned by `HSCAN` can be used to resume the scan from the last position, making it suitable for use cases where the scan needs to be interrupted and resumed later. diff --git a/docs/src/content/docs/commands/JSON.GET.md b/docs/src/content/docs/commands/JSON.GET.md index 9e64a4b8d..7353dfc11 100644 --- a/docs/src/content/docs/commands/JSON.GET.md +++ b/docs/src/content/docs/commands/JSON.GET.md @@ -13,12 +13,21 @@ JSON.GET [path] ## Parameters -- `key`: (Required) The key under which the JSON data is stored in DiceDB. -- `path`: (Optional) A JSONPath expression to specify the part of the JSON document to retrieve. If not provided, the entire JSON document is returned. +| Parameter | Description | Type | Required | +|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------| +| `key` | The key against which the JSON data is stored in DiceDB | String | Yes | +| `path` | A JSONPath expression to specify the part of the JSON document to retrieve. If not provided, the entire JSON document is returned. Default value is **$** (root) | String | No | -## Return Value -The command returns the JSON data stored at the specified key and path. The data is returned as a JSON string. If the specified key or path does not exist, the command returns `nil`. +## Return Values + +| Condition | Return Value | +| ---------------------------------------------------------------------------- | ---------------------------------------------------- | +| The specified key does not exists | `nil` | +| The specified key exists and path argument is not specified | `String`: The entire JSON data for the key | +| The specified key exists and the specified path exists in the JSON data | `String`: The data for the key at the specified path | +| The specified key exists and specified path does not exists in the JSON data | `nil` | +| Syntax or specified constraints are invalid | error | ## Behaviour @@ -29,17 +38,16 @@ When the `JSON.GET` command is executed: 3. If a path is provided, DiceDB extracts the specified part of the JSON document using the JSONPath expression. 4. The retrieved JSON data is returned as a JSON string. -## Error Handling - -The `JSON.GET` command can raise the following errors: +## Errors -- `(error) ERR wrong number of arguments for 'JSON.GET' command`: This error occurs if the command is called with an incorrect number of arguments. -- `(error) ERR key does not exist`: This error occurs if the specified key does not exist in the DiceDB database. -- `(error) ERR invalid path`: This error occurs if the provided JSONPath expression is invalid or does not match any part of the JSON document. +1. `Incorrect number of arguments` + - Error Message: `(error) ERR wrong number of arguments for 'json.get' command` +2. `Invalid JSONPath expression` + - Error Message: `(error) ERR invalid JSONPath` ## Example Usage -### Example 1: Retrieve Entire JSON Document +### Retrieve Entire JSON Document ```bash 127.0.0.1:7379> JSON.SET user:1001 $ '{"name": "John Doe", "age": 30, "email": "john.doe@example.com"}' @@ -48,7 +56,7 @@ OK "{\"name\":\"John Doe\",\"age\":30,\"email\":\"john.doe@example.com\"}" ``` -### Example 2: Retrieve Specific Field from JSON Document +### Retrieve Specific Field from JSON Document ```bash 127.0.0.1:7379> JSON.SET user:1001 $ '{"name": "John Doe", "age": 30, "email": "john.doe@example.com"}' @@ -57,7 +65,7 @@ OK "\"John Doe\"" ``` -### Example 3: Retrieve Nested Field from JSON Document +### Retrieve Nested Field from JSON Document ```bash 127.0.0.1:7379> JSON.SET user:1002 $ '{"name": "Jane Doe", "address": {"city": "New York", "zip": "10001"}}' @@ -66,14 +74,14 @@ OK "\"New York\"" ``` -### Example 4: Handling Non-Existent Key +### Handling Non-Existent Key ```bash 127.0.0.1:7379> JSON.GET user:9999 (nil) ``` -### Example 5: Handling Invalid Path +### Handling Non-Existent Path ```bash 127.0.0.1:7379> JSON.SET user:1001 $ '{"name": "John Doe", "age": 30, "email": "john.doe@example.com"}' @@ -86,4 +94,4 @@ OK - JSONPath expressions allow you to navigate and retrieve specific parts of a JSON document. Ensure that your JSONPath expressions are correctly formatted to avoid errors. -By understanding the `JSON.GET` command, you can efficiently retrieve JSON data stored in your DiceDB database, enabling you to build powerful and flexible applications that leverage the capabilities of DiceDBJSON. +By understanding the `JSON.GET` command, you can efficiently retrieve JSON data stored in your DiceDB database, enabling you to build powerful and flexible applications that leverage the capabilities of DiceDB. diff --git a/docs/src/content/docs/commands/KEYS.md b/docs/src/content/docs/commands/KEYS.md index 56c9de77d..ed73b3f58 100644 --- a/docs/src/content/docs/commands/KEYS.md +++ b/docs/src/content/docs/commands/KEYS.md @@ -13,65 +13,94 @@ KEYS pattern ## Parameters -- `pattern`: A string that represents the pattern to match against the keys in the DiceDB database. The pattern can include special glob-style characters: - - `*` matches any number of characters (including zero). - - `?` matches exactly one character. - - `[abc]` matches any one of the characters inside the brackets. - - `[a-z]` matches any character in the specified range. +| Parameter | Description | Type | Required | +|-----------------|--------------------------------------------------|---------|----------| +| `pattern` | A string representing the pattern to match against the keys in DiceDB databse.| String | Yes | -## Return Value +Supported glob-style characters: + - `*` matches any number of characters (including zero). + - `?` matches exactly one character. + - `[abc]` matches any one of the characters inside the brackets. + - `[a-z]` matches any character in the specified range. -The `KEYS` command returns an array of strings, where each string is a key that matches the specified pattern. If no keys match the pattern, an empty array is returned. +Use `\` to escape special characters if you want to match them verbatim. -## Example Usage +## Return values -### Basic Example +| Condition | Return Value | +|------------------------------------------------|---------------------------------------------------| +| Command is successful | Array of strings, where each string is key that matches the specified pattern.| +| Command is successful but key not found | `(empty list or set)` | +| Syntax or specified constraints are invalid | error | + +## Example usage + +### Basic example ```plaintext -DiceDB> SET key1 "value1" +127.0.0.1:7379> SET key1 "value1" OK -DiceDB> SET key2 "value2" +127.0.0.1:7379> SET key2 "value2" OK -DiceDB> SET anotherkey "value3" +127.0.0.1:7379> SET anotherkey "value3" OK -DiceDB> KEYS key* +127.0.0.1:7379> KEYS key* 1) "key1" 2) "key2" ``` -### Using Wildcards +### Using wildcards ```plaintext -DiceDB> SET key1 "value1" +127.0.0.1:7379> SET key1 "value1" OK -DiceDB> SET key2 "value2" +127.0.0.1:7379> SET key2 "value2" OK -DiceDB> SET key3 "value3" +127.0.0.1:7379> SET key3 "value3" OK -DiceDB> KEYS key? -1) "key1" -2) "key2" -3) "key3" +127.0.0.1:7379> KEYS key? +1) "key3" +2) "key1" +3) "key2" ``` -### Using Character Ranges +### Using character ranges ```plaintext -DiceDB> SET key1 "value1" +127.0.0.1:7379> SET key1 "value1" OK -DiceDB> SET key2 "value2" +127.0.0.1:7379> SET key2 "value2" OK -DiceDB> SET key3 "value3" +127.0.0.1:7379> SET key3 "value3" OK -DiceDB> KEYS key[1-2] +127.0.0.1:7379> KEYS key[1-2] 1) "key1" 2) "key2" ``` +### Using \ to escape special characters + +```plaintext +127.0.0.1:7379> SET key1 "value1" +OK +127.0.0.1:7379> SET key2 "value2" +OK +127.0.0.1:7379> SET key3 "value3" +OK +127.0.0.1:7379> KEYS key? +1) "key3" +2) "key*" +3) "key?" +127.0.0.1:7379> KEYS key\? +1) "key?" +``` + ## Behaviour When the `KEYS` command is executed, DiceDB scans the entire keyspace to find all keys that match the specified pattern. This operation is performed in a non-blocking manner, but it can still be slow if the keyspace is large. Therefore, it is generally not recommended to use the `KEYS` command in a production environment where performance is critical. +Additionally, the ordering of the output keys can be different if you run the same command subsequently. + ## Error Handling The `KEYS` command is straightforward and does not have many error conditions. However, there are a few scenarios where errors might occur: @@ -94,11 +123,8 @@ The `KEYS` command is straightforward and does not have many error conditions. H - `SCAN`: The `SCAN` command is a cursor-based iterator that allows you to incrementally iterate over the keyspace without blocking the server. It is a more efficient alternative to `KEYS` for large datasets. ```plaintext -DiceDB> SCAN 0 MATCH key* +127.0.0.1:7379> SCAN 0 MATCH key* 1) "0" 2) 1) "key1" 2) "key2" ``` - -By following this detailed documentation, you should be able to effectively use the `KEYS` command in DiceDB while understanding its limitations and best practices. - diff --git a/docs/src/content/docs/commands/MGET.md b/docs/src/content/docs/commands/MGET.md index 893616508..132ecded7 100644 --- a/docs/src/content/docs/commands/MGET.md +++ b/docs/src/content/docs/commands/MGET.md @@ -13,39 +13,61 @@ MGET key [key ...] ## Parameters -- `key [key ...]`: One or more keys for which the values need to be retrieved. Each key is a string. +| Parameter | Description | Type | Required | +|------------|---------------------------------------------------------|---------|----------| +| `key` | One or more keys for which the values need to be retrieved. Each key is a string. |String | Yes | + ## Return Value The `MGET` command returns an array of values corresponding to the specified keys. If a key does not exist, the corresponding value in the array will be `nil`. -## Behaviour +| Condition | Return Value | +|-----------------------------------|-------------------------------------------------------------| +| All specified keys exist | An array of values corresponding to the specified keys | +| Some keys do not exist | An array where non-existent keys have `nil` as their value | +| Keys are of the wrong type | error | -When the `MGET` command is executed, DiceDB will: +## Behaviour -1. Look up each specified key in the database. -2. Retrieve the value associated with each key. -3. Return an array of values in the same order as the keys were specified. +- The `MGET` command retrieves the values of multiple specified keys in the same order as they are listed in the command. +- If a key does not exist, its corresponding position in the returned array will contain `nil`. +- If any of the keys hold non-string values (e.g., lists or sets), the `MGET` command will return an error indicating that the operation was attempted on the wrong type of key. -If a key does not exist, the corresponding position in the returned array will contain `nil`. ## Error Handling The `MGET` command can raise errors in the following scenarios: -1. `Wrong Type Error`: If any of the specified keys exist but are not of the string type, a `WRONGTYPE` error will be raised. -2. `Syntax Error`: If the command is not used with at least one key, a `syntax error` will be raised. +1. `Wrong type of value or key`: + - `Error Message`: `(error) WRONGTYPE Operation against a key holding the wrong kind of value` + - Occurs when one of the keys holds a value that is not a string. + +2. `Invalid syntax`: + - `Error Message`: `(error) ERR wrong number of arguments for 'mget' command` + - Occurs when the command is issued without any keys. ## Example Usage ### Basic Example -```DiceDB -SET key1 "value1" -SET key2 "value2" -SET key3 "value3" +Retrieving the values of multiple keys `key1`, `key2`, and `key3`: + -MGET key1 key2 key3 +```bash +127.0.0.1:7379> SET key1 "value1" +OK +``` +```bash +127.0.0.1:7379> SET key2 "value2" +OK +``` +```bash +127.0.0.1:7379> SET key3 "value3" +OK +``` +```bash +127.0.0.1:7379> MGET key1 key2 key3 ``` `Output:` @@ -58,13 +80,17 @@ MGET key1 key2 key3 ### Example with Non-Existent Keys -```DiceDB -SET key1 "value1" -SET key2 "value2" - -MGET key1 key2 key3 +```bash +127.0.0.1:7379> SET key1 "value1" +OK +``` +```bash +127.0.0.1:7379> SET key2 "value2" +OK +``` +```bash +127.0.0.1:7379> MGET key1 key2 key3 ``` - `Output:` ``` @@ -75,17 +101,50 @@ MGET key1 key2 key3 ### Example with Mixed Types -```DiceDB -SET key1 "value1" -LPUSH key2 "value2" +###### Key2 doesn't exist previously + +```bash +127.0.0.1:7379> SET key1 "value1" +OK +``` +```bash +127.0.0.1:7379> LPUSH key2 "value2" +(integer) 1 +``` +```bash +127.0.0.1:7379> MGET key1 key2 +``` + +`Output:` + +``` +1) "value1" +2) (nil) +``` + +###### Key2 exists previously with different datatype -MGET key1 key2 +```bash +127.0.0.1:7379> SET key1 "value1" +OK +``` +```bash +127.0.0.1:7379> SET key2 "value2" +OK +``` +```bash +127.0.0.1:7379> LPUSH key2 "value3" +(error) ERROR WRONGTYPE Operation against a key holding the wrong kind of value +``` +```bash +127.0.0.1:7379> MGET key1 key2 ``` `Output:` ``` -(error) WRONGTYPE Operation against a key holding the wrong kind of value +1) "value1" +2) "value2" ``` ## Notes @@ -101,4 +160,3 @@ MGET key1 key2 - Handle `nil` values in the returned array appropriately in your application logic to account for non-existent keys. By following this documentation, you should be able to effectively use the `MGET` command in DiceDB to retrieve multiple values efficiently and handle any potential errors that may arise. - diff --git a/docs/src/content/docs/commands/PERSIST.md b/docs/src/content/docs/commands/PERSIST.md index 7c513787c..747df3a5a 100644 --- a/docs/src/content/docs/commands/PERSIST.md +++ b/docs/src/content/docs/commands/PERSIST.md @@ -13,36 +13,52 @@ PERSIST key ## Parameters -- `key`: The key for which the expiration should be removed. This is a required parameter and must be a valid key in the DiceDB database. +| Parameter | Description | Type | Required | +|-----------|---------------------------------------------------------------------------|---------|----------| +| `key` | The name of the key to persist. | String | Yes | ## Return Value -- `Integer reply`: The command returns an integer: - - `1` if the timeout was successfully removed. - - `0` if the key does not exist or does not have an associated timeout. +| Condition | Return Value | +|------------------------------------------------|---------------------------------------------------| +| The timeout was successfully removed | `1` | +| The key does not exist or does not have a timeout| `0` | ## Behaviour When the `PERSIST` command is executed: -1. DiceDB checks if the specified key exists in the database. -2. If the key exists and has an expiration time, the expiration time is removed, making the key persistent. -3. If the key does not exist or does not have an expiration time, no changes are made. +1. If the specified key has an expiration time, the `PERSIST` command removes that expiration, making the key persistent. +2. If the key does not exist or does not have an expiration, the command does nothing and returns `0`. +3. This command does not alter the key’s value, only its expiration state. ## Error Handling -- `WRONGTYPE Operation against a key holding the wrong kind of value`: This error is raised if the key exists but is not of a type that supports expiration (e.g., a key holding a stream). -- `(nil)`: This is not an error but an indication that the key does not exist or does not have an expiration time. +1. `Wrong type of value or key`: + - Error Message: `(error) WRONGTYPE Operation against a key holding the wrong kind of value` + - Occurs when attempting to use the command on a key that contains a non-string value or one that does not support expiration. +2. `No timeout to persist`: + - This is not an error but occurs when the key either does not exist or does not have an expiration time. The command will return `0` in such cases. ## Example Usage ### Example 1: Removing Expiration from a Key -```DiceDB -SET mykey "Hello" -EXPIRE mykey 10 -PERSIST mykey -TTL mykey +```bash +127.0.0.1:7379> SET mykey "Hello" +OK +``` +```bash +127.0.0.1:7379> EXPIRE mykey 10 +(integer) 1 +``` +```bash +127.0.0.1:7379> PERSIST mykey +(integer) 1 +``` +```bash +127.0.0.1:7379> TTL mykey +(integer) -1 ``` `Explanation`: @@ -54,42 +70,31 @@ TTL mykey ### Example 2: Attempting to Persist a Non-Existent Key -```DiceDB -PERSIST nonExistentKey +```bash +127.0.0.1:7379> PERSIST mykey +(integer) 0 ``` `Explanation`: -- The command returns `0` because `nonExistentKey` does not exist in the database. +- The command returns `0` because `mykey` does not exist in the database. ### Example 3: Persisting a Key Without Expiration -```DiceDB -SET mykey "Hello" -PERSIST mykey +```bash +127.0.0.1:7379> SET mykey +OK +``` + +```bash +127.0.0.1:7379> PERSIST mykey +(integer) 0 ``` `Explanation`: - The command returns `0` because `mykey` does not have an expiration time set. -## Error Handling - -### Case 1: Key Does Not Exist - -If the key does not exist, the `PERSIST` command will return `0` and no error will be raised. - -### Case 2: Key Does Not Have an Expiration - -If the key exists but does not have an expiration time, the `PERSIST` command will return `0` and no error will be raised. - -### Case 3: Key Holds the Wrong Kind of Value - -If the key exists but holds a value type that does not support expiration (e.g., a stream), the command will raise an error: - -``` -(error) WRONGTYPE Operation against a key holding the wrong kind of value -``` ## Summary diff --git a/docs/src/content/docs/commands/PTTL.md b/docs/src/content/docs/commands/PTTL.md index 8a692cf67..f98e008e2 100644 --- a/docs/src/content/docs/commands/PTTL.md +++ b/docs/src/content/docs/commands/PTTL.md @@ -1,72 +1,63 @@ --- title: PTTL -description: Documentation for the DiceDB command PTTL +description: The `PTTL` command in DiceDB is used to retrieve the remaining time to live (TTL) of a key in milliseconds. This command is particularly useful for understanding how much longer a key will exist before it expires. If the key does not have an associated expiration, the command will return a specific value indicating this state. --- The `PTTL` command in DiceDB is used to retrieve the remaining time to live (TTL) of a key in milliseconds. This command is particularly useful for understanding how much longer a key will exist before it expires. If the key does not have an associated expiration, the command will return a specific value indicating this state. ## Syntax -```plaintext +``` PTTL key ``` ## Parameters -- `key`: The key for which the remaining TTL is to be retrieved. This parameter is mandatory and must be a valid key in the DiceDB database. +| Parameter | Description | Type | Required | +|-----------------|--------------------------------------------------------------------------|---------|----------| +| `key` | The key for which the remaining TTL is to be retrieved. | String | Yes | -## Return Value +## Return values -The `PTTL` command returns an integer value representing the remaining time to live of the key in milliseconds. The possible return values are: +| Condition | Return Value | +|------------------------------------------------------------|-------------------| +| Command is successful | Returns a positive integer value representing the remaining time to live of the key in milliseconds | +| The key exists but has no associated expiration | `-1` | +| The key does not exist | `-2` | -- A positive integer: The remaining TTL in milliseconds. -- `-1`: The key exists but has no associated expiration. -- `-2`: The key does not exist. ## Behaviour -When the `PTTL` command is executed, DiceDB checks the specified key to determine its remaining TTL. The command will return the TTL in milliseconds if the key exists and has an expiration. If the key exists but does not have an expiration, the command will return `-1`. If the key does not exist, the command will return `-2`. - -## Error Handling +- The command is non-destructive and does not modify the key or its expiration in any way. +- If a key's TTL is modified (e.g., by `EXPIRE` or `PEXPIRE` commands), subsequent `PTTL` calls will reflect the updated remaining time. -The `PTTL` command can raise errors in the following scenarios: -1. `Wrong number of arguments`: If the command is called without the required number of arguments, DiceDB will return an error. +## Errors - - `Error Message`: `ERR wrong number of arguments for 'pttl' command` +1. `Wrong number of arguments`: -2. `Invalid key type`: If the key is not a string, DiceDB will return an error. + - Error Message: `(error) ERR wrong number of arguments for 'pttl' command` + - Occures when attempting to use this command without any arguments or with more than one argument. - - `Error Message`: `WRONGTYPE Operation against a key holding the wrong kind of value` ## Example Usage -### Example 1: Key with TTL +### Example 1: Key with Expiration -```plaintext -SET mykey "Hello" -PEXPIRE mykey 5000 -PTTL mykey +```bash +127.0.0.1:7379> SET mykey "Hello" +127.0.0.1:7379> EXPIRE mykey 10 +127.0.0.1:7379> PTTL mykey +(integer) 10000 ``` -`Output:` - -```plaintext -(integer) 5000 -``` +In this example, the key `mykey` is set with a value of "Hello" and an expiration of 10 seconds. The `PTTL` command returns `10000`, indicating that the key will expire in 10000 milliseconds. -In this example, the key `mykey` is set with a value of "Hello" and an expiration of 5000 milliseconds. The `PTTL` command returns `5000`, indicating that the key will expire in 5000 milliseconds. - -### Example 2: Key without TTL - -```plaintext -SET mykey "Hello" -PTTL mykey -``` +### Example 2: Key without Expiration -`Output:` - -```plaintext +```bash +127.0.0.1:7379> SET mykey "Hello" +127.0.0.1:7379> PTTL mykey (integer) -1 ``` @@ -74,14 +65,19 @@ In this example, the key `mykey` is set with a value of "Hello" but no expiratio ### Example 3: Non-existent Key -```plaintext -PTTL nonExistentKey +```bash +127.0.0.1:7379> PTTL nonExistentKey +(integer) -2 ``` -`Output:` +In this example, the key `nonExistentKey` does not exist in the DiceDB database. The `PTTL` command returns `-2`, indicating that the key does not exist. + +### Example 4: Invalid usage -```plaintext -(integer) -2 +```bash +127.0.0.1:7379> SET newkey "value" +127.0.0.1:7379> PTTL newkey value +(error) ERR wrong number of arguments for 'pttl' command ``` -In this example, the key `nonExistentKey` does not exist in the DiceDB database. The `PTTL` command returns `-2`, indicating that the key does not exist. +In this example, the `PTTL` command is used with an extra argument. This results in an error, as the `PTTL` command accepts only one argument. diff --git a/docs/src/content/docs/commands/SCARD.md b/docs/src/content/docs/commands/SCARD.md index 4022f0356..7fcedd932 100644 --- a/docs/src/content/docs/commands/SCARD.md +++ b/docs/src/content/docs/commands/SCARD.md @@ -7,66 +7,92 @@ The `SCARD` command in DiceDB is used to get the number of members in a set. Thi ## Syntax -```plaintext +``` SCARD key ``` ## Parameters -- `key`: The key of the set whose cardinality (number of members) you want to retrieve. The key must be a valid string. +| Parameter | Description | Type | Required | +|------------|---------------------------------------------------------------------------------|--------|----------| +| `key` | The key of the set whose cardinality (number of members) you want to retrieve. | String | Yes | + + +## Return Values -## Return Value +| Condition | Return Value | +|-----------------------------------------|----------------------------------| +| Key of Set type exists | Number of elements in the set | +| Key doesn't exist | `0` | +| Invalid syntax/key is of the wrong type | error | -- `Integer`: The number of elements in the set, or 0 if the set does not exist. ## Behaviour When the `SCARD` command is executed, DiceDB will: 1. Check if the key exists. -2. If the key does not exist, it will return 0. -3. If the key exists but is not a set, an error will be returned. -4. If the key exists and is a set, it will return the number of elements in the set. +2. If the key exists and is a set, it will return the number of elements in the set. +3. If the key does not exist, it will return 0. +4. If the key exists but is not a set, an error will be returned. + +## Errors + +1. `Wrong type of key`: -## Error Handling + - Error Message: `(error) ERROR WRONGTYPE Operation against a key holding the wrong kind of value` + - Occurs if the key exists but is not a set. DiceDB expects the key to be associated with a set data type. If the key is associated with a different data type (e.g., a string, list, hash, or sorted set), this error will be raised. -The `SCARD` command can raise the following errors: +2. `Wrong number of arguments`: -- `WRONGTYPE Operation against a key holding the wrong kind of value`: This error occurs if the key exists but is not a set. DiceDB expects the key to be associated with a set data type. If the key is associated with a different data type (e.g., a string, list, hash, or sorted set), this error will be raised. + - Error Message: `(error) ERROR wrong number of arguments for 'scard' command` + - Occurs if wrong number of keys is passed to the command, such as passing more than 1 key, or passing no key. -## Example Usage -### Example 1: Basic Usage +## Examples -```plaintext -DiceDB> SADD myset "apple" +### Basic Example + +Add three members to the set `myset` and then get the cardinality of the set using `SCARD` command. + +```bash +127.0.0.1:7379> SADD myset "apple" (integer) 1 -DiceDB> SADD myset "banana" +127.0.0.1:7379> SADD myset "banana" (integer) 1 -DiceDB> SADD myset "cherry" +127.0.0.1:7379> SADD myset "cherry" (integer) 1 -DiceDB> SCARD myset +127.0.0.1:7379> SCARD myset (integer) 3 ``` -In this example, we first add three members to the set `myset`. Then, we use the `SCARD` command to get the number of members in the set, which returns 3. +### Non-Existent Key -### Example 2: Non-Existent Key +Get the cardinality of a set that does not exist. -```plaintext -DiceDB> SCARD nonexistingset +```bash +127.0.0.1:7379> SCARD nonexistingset (integer) 0 ``` -In this example, we attempt to get the cardinality of a set that does not exist. The `SCARD` command returns 0, indicating that the set is empty or does not exist. +### Error Example: Wrong Type -### Example 3: Wrong Type Error +Get the cardinality of a key holding string value -```plaintext -DiceDB> SET mystring "hello" +```bash +127.0.0.1:7379> SET mystring "hello" OK -DiceDB> SCARD mystring -(error) WRONGTYPE Operation against a key holding the wrong kind of value +127.0.0.1:7379> SCARD mystring +(error) ERROR WRONGTYPE Operation against a key holding the wrong kind of value ``` -In this example, we first set a string value to the key `mystring`. When we attempt to use the `SCARD` command on this key, DiceDB returns an error because `mystring` is not a set. +### Error Example: Wrong Argument + +Get cardinality of a set holding 0 or more than 1 argument + +```bash +127.0.0.1:7379> SCARD +(error) ERROR wrong number of arguments for 'scard' command +127.0.0.1:7379> SCARD myset1 myset2 +(error) ERROR wrong number of arguments for 'scard' command +``` \ No newline at end of file diff --git a/go.mod b/go.mod index 38e68ce3e..df97f9fb0 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( github.com/cockroachdb/swiss v0.0.0-20240612210725-f4de07ae6964 github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da github.com/dicedb/go-dice v0.0.0-20240820180649-d97f15fca831 + github.com/gobwas/glob v0.2.3 github.com/google/btree v1.1.3 github.com/google/go-cmp v0.6.0 github.com/gorilla/websocket v1.5.3 diff --git a/go.sum b/go.sum index aaaed3a20..58d2b7c56 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= diff --git a/integration_tests/commands/async/del_test.go b/integration_tests/commands/async/del_test.go index 4f0284523..2e31fa76d 100644 --- a/integration_tests/commands/async/del_test.go +++ b/integration_tests/commands/async/del_test.go @@ -30,6 +30,11 @@ func TestDel(t *testing.T) { commands: []string{"GET k3", "DEL k3"}, expected: []interface{}{"(nil)", int64(0)}, }, + { + name: "DEL with no keys or arguments", + commands: []string{"DEL"}, + expected: []interface{}{"ERR wrong number of arguments for 'del' command"}, + }, } for _, tc := range testCases { @@ -45,4 +50,4 @@ func TestDel(t *testing.T) { } }) } -} +} \ No newline at end of file diff --git a/integration_tests/commands/async/hscan_test.go b/integration_tests/commands/async/hscan_test.go new file mode 100644 index 000000000..bdbfdd74a --- /dev/null +++ b/integration_tests/commands/async/hscan_test.go @@ -0,0 +1,91 @@ +package async + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +var THREE int64 = 3 +var FOUR int64 = 4 + +func TestHSCAN(t *testing.T) { + conn := getLocalConnection() + defer conn.Close() + + testCases := []TestCase{ + { + commands: []string{"HSCAN empty_hash 0"}, + expected: []interface{}{[]interface{}{"0", []interface{}{}}}, + }, + { + commands: []string{ + "HSET test_hash field1 value1 field2 value2 field3 value3", + "HSCAN test_hash 0", + }, + expected: []interface{}{ + THREE, + []interface{}{ + "0", + []interface{}{"field1", "value1", "field2", "value2", "field3", "value3"}, + }, + }, + }, + { + commands: []string{ + "HSET pattern_hash foo1 bar1 foo2 bar2 baz1 qux1 baz2 qux2", + "HSCAN pattern_hash 0 MATCH foo*", + }, + expected: []interface{}{ + FOUR, + []interface{}{ + "0", + []interface{}{"foo1", "bar1", "foo2", "bar2"}, + }, + }, + }, + { + commands: []string{ + "HSET large_hash field1 value1 field2 value2", + "HSCAN large_hash 0 COUNT 2", + }, + expected: []interface{}{ + TWO, + []interface{}{"0", []interface{}{"field1", "value1", "field2", "value2"}}, + }, + }, + { + commands: []string{ + "SET wrong_type_key string_value", + "HSCAN wrong_type_key 0", + }, + expected: []interface{}{ + "OK", + "WRONGTYPE Operation against a key holding the wrong kind of value", + }, + }, + { + commands: []string{"HSCAN"}, + expected: []interface{}{"ERR wrong number of arguments for 'hscan' command"}, + }, + { + commands: []string{ + "HSET test_hash1 field1 value1 field2 value2 field3 value3 field4 value4", + "HSCAN test_hash1 0 COUNT 2", + "HSCAN test_hash1 2 COUNT 2", + }, + expected: []interface{}{ + FOUR, + []interface{}{"2", []interface{}{"field1", "value1", "field2", "value2"}}, + []interface{}{"0", []interface{}{"field3", "value3", "field4", "value4"}}, + }, + }, + } + + for _, tc := range testCases { + for i, cmd := range tc.commands { + result := FireCommand(conn, cmd) + assert.DeepEqual(t, tc.expected[i], result) + } + } +} diff --git a/integration_tests/commands/async/setup.go b/integration_tests/commands/async/setup.go index ec359f8f1..1d7d07c76 100644 --- a/integration_tests/commands/async/setup.go +++ b/integration_tests/commands/async/setup.go @@ -120,7 +120,7 @@ func RunTestServer(ctx context.Context, wg *sync.WaitGroup, opt TestServerOption const totalRetries = 100 var err error - watchChan := make(chan dstore.QueryWatchEvent, config.DiceConfig.Server.KeysLimit) + watchChan := make(chan dstore.QueryWatchEvent, config.DiceConfig.Server.WatchChanBufSize) gec := make(chan error) shardManager := shard.NewShardManager(1, watchChan, nil, gec, opt.Logger) // Initialize the AsyncServer diff --git a/integration_tests/commands/http/setup.go b/integration_tests/commands/http/setup.go index a10526cb3..0fc1a8deb 100644 --- a/integration_tests/commands/http/setup.go +++ b/integration_tests/commands/http/setup.go @@ -102,7 +102,7 @@ func RunHTTPServer(ctx context.Context, wg *sync.WaitGroup, opt TestServerOption config.DiceConfig.Server.WriteAOFOnCleanup = false globalErrChannel := make(chan error) - watchChan := make(chan dstore.QueryWatchEvent, config.DiceConfig.Server.KeysLimit) + watchChan := make(chan dstore.QueryWatchEvent, config.DiceConfig.Server.WatchChanBufSize) shardManager := shard.NewShardManager(1, watchChan, nil, globalErrChannel, opt.Logger) queryWatcherLocal := querymanager.NewQueryManager(opt.Logger) config.HTTPPort = opt.Port diff --git a/integration_tests/commands/websocket/setup.go b/integration_tests/commands/websocket/setup.go index 10f718c77..0947d9997 100644 --- a/integration_tests/commands/websocket/setup.go +++ b/integration_tests/commands/websocket/setup.go @@ -98,7 +98,7 @@ func RunWebsocketServer(ctx context.Context, wg *sync.WaitGroup, opt TestServerO // Initialize the WebsocketServer globalErrChannel := make(chan error) - watchChan := make(chan dstore.QueryWatchEvent, config.DiceConfig.Server.KeysLimit) + watchChan := make(chan dstore.QueryWatchEvent, config.DiceConfig.Server.WatchChanBufSize) shardManager := shard.NewShardManager(1, watchChan, nil, globalErrChannel, opt.Logger) config.WebsocketPort = opt.Port testServer := server.NewWebSocketServer(shardManager, watchChan, opt.Logger) diff --git a/internal/eval/commands.go b/internal/eval/commands.go index 4c512b757..96593ec47 100644 --- a/internal/eval/commands.go +++ b/internal/eval/commands.go @@ -693,6 +693,16 @@ var ( Arity: -3, KeySpecs: KeySpecs{BeginIndex: 1}, } + hscanCmdMeta = DiceCmdMeta{ + Name: "HSCAN", + Info: `HSCAN is used to iterate over fields and values of a hash. + It returns a cursor and a list of key-value pairs. + The cursor is used to paginate through the hash. + The command returns a cursor value of 0 when all the elements are iterated.`, + Eval: evalHSCAN, + Arity: -3, + KeySpecs: KeySpecs{BeginIndex: 1}, + } hexistsCmdMeta = DiceCmdMeta{ Name: "HEXISTS", Info: `Returns if field is an existing field in the hash stored at key.`, @@ -1170,6 +1180,7 @@ func init() { DiceCmds["SETEX"] = setexCmdMeta DiceCmds["HRANDFIELD"] = hrandfieldCmdMeta DiceCmds["HDEL"] = hdelCmdMeta + DiceCmds["HSCAN"] = hscanCmdMeta DiceCmds["HVALS"] = hValsCmdMeta DiceCmds["APPEND"] = appendCmdMeta DiceCmds["ZADD"] = zaddCmdMeta diff --git a/internal/eval/eval.go b/internal/eval/eval.go index 7e75fda43..f41d99330 100644 --- a/internal/eval/eval.go +++ b/internal/eval/eval.go @@ -37,6 +37,7 @@ import ( "github.com/dicedb/dice/internal/querymanager" "github.com/dicedb/dice/internal/server/utils" dstore "github.com/dicedb/dice/internal/store" + "github.com/gobwas/glob" "github.com/ohler55/ojg/jp" ) @@ -67,6 +68,7 @@ const ( const defaultRootPath = "$" const maxExDuration = 9223372036854775 +const CountConst = "COUNT" func init() { diceCommandsCount = len(DiceCmds) @@ -169,8 +171,11 @@ func evalDBSIZE(args []string, store *dstore.Store) []byte { return diceerrors.NewErrArity("DBSIZE") } + // Expired keys must be explicitly deleted since the cronFrequency for cleanup is configurable. + // A longer delay may prevent timely cleanup, leading to incorrect DBSIZE results. + dstore.DeleteExpiredKeys(store) // return the RESP encoded value - return clientio.Encode(store.GetKeyCount(), false) + return clientio.Encode(store.GetDBSize(), false) } // evalGETDEL returns the value for the queried key in args @@ -1640,6 +1645,10 @@ func evalTTL(args []string, store *dstore.Store) []byte { func evalDEL(args []string, store *dstore.Store) []byte { var countDeleted = 0 + if len(args) < 1 { + return diceerrors.NewErrArity("DEL") + } + for _, key := range args { if ok := store.Del(key); ok { countDeleted++ @@ -2594,7 +2603,7 @@ func evalCommandHelp() []byte { format := "COMMAND [ [value] [opt] ...]. Subcommands are:" noTitle := "(no subcommand)" noMessage := " Return details about all Dice commands." - countTitle := "COUNT" + countTitle := CountConst countMessage := " Return the total number of commands in this Dice server." listTitle := "LIST" listMessage := " Return a list of all commands in this Dice server." @@ -3238,6 +3247,85 @@ func evalHDEL(args []string, store *dstore.Store) []byte { return clientio.Encode(count, false) } +func evalHSCAN(args []string, store *dstore.Store) []byte { + if len(args) < 2 { + return diceerrors.NewErrArity("HSCAN") + } + + key := args[0] + cursor, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return diceerrors.NewErrWithMessage(diceerrors.InvalidIntErr) + } + + obj := store.Get(key) + if obj == nil { + return clientio.Encode([]interface{}{"0", []string{}}, false) + } + + if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil { + return diceerrors.NewErrWithMessage(diceerrors.WrongTypeErr) + } + + hashMap := obj.Value.(HashMap) + pattern := "*" + count := 10 + + // Parse optional arguments + for i := 2; i < len(args); i += 2 { + switch strings.ToUpper(args[i]) { + case "MATCH": + if i+1 < len(args) { + pattern = args[i+1] + } + case CountConst: + if i+1 < len(args) { + parsedCount, err := strconv.Atoi(args[i+1]) + if err != nil || parsedCount < 1 { + return diceerrors.NewErrWithMessage("value is not an integer or out of range") + } + count = parsedCount + } + } + } + + // Note that this implementation has a time complexity of O(N), where N is the number of keys in 'hashMap'. + // This is in contrast to Redis, which implements HSCAN in O(1) time complexity by maintaining a cursor. + keys := make([]string, 0, len(hashMap)) + for k := range hashMap { + keys = append(keys, k) + } + sort.Strings(keys) + + matched := 0 + results := make([]string, 0, count*2) + newCursor := 0 + + g, err := glob.Compile(pattern) + if err != nil { + return diceerrors.NewErrWithMessage(fmt.Sprintf("Invalid glob pattern: %s", err)) + } + + // Scan the keys and add them to the results if they match the pattern + for i := int(cursor); i < len(keys); i++ { + if g.Match(keys[i]) { + results = append(results, keys[i], hashMap[keys[i]]) + matched++ + if matched >= count { + newCursor = i + 1 + break + } + } + } + + // If we've scanned all keys, reset cursor to 0 + if newCursor >= len(keys) { + newCursor = 0 + } + + return clientio.Encode([]interface{}{strconv.Itoa(newCursor), results}, false) +} + // evalHKEYS returns all the values in the hash stored at key. func evalHVALS(args []string, store *dstore.Store) []byte { if len(args) != 1 { diff --git a/internal/eval/eval_test.go b/internal/eval/eval_test.go index a068a9581..2ff275a17 100644 --- a/internal/eval/eval_test.go +++ b/internal/eval/eval_test.go @@ -82,6 +82,7 @@ func TestEval(t *testing.T) { testEvalHSTRLEN(t, store) testEvalHEXISTS(t, store) testEvalHDEL(t, store) + testEvalHSCAN(t, store) testEvalPFMERGE(t, store) testEvalJSONSTRLEN(t, store) testEvalJSONOBJLEN(t, store) @@ -1937,12 +1938,12 @@ func testEvalDel(t *testing.T, store *dstore.Store) { "nil value": { setup: func() {}, input: nil, - output: []byte(":0\r\n"), + output: clientio.Encode(errors.New("ERR wrong number of arguments for 'del' command"), false), }, "empty array": { setup: func() {}, input: []string{}, - output: []byte(":0\r\n"), + output: clientio.Encode(errors.New("ERR wrong number of arguments for 'del' command"), false), }, "key does not exist": { setup: func() {}, @@ -2428,6 +2429,91 @@ func testEvalHDEL(t *testing.T, store *dstore.Store) { runEvalTests(t, tests, evalHDEL, store) } +func testEvalHSCAN(t *testing.T, store *dstore.Store) { + tests := map[string]evalTestCase{ + "HSCAN with wrong number of args": { + input: []string{"key"}, + output: []byte("-ERR wrong number of arguments for 'hscan' command\r\n"), + }, + "HSCAN with key does not exist": { + input: []string{"NONEXISTENT_KEY", "0"}, + output: []byte("*2\r\n$1\r\n0\r\n*0\r\n"), + }, + "HSCAN with key exists but not a hash": { + setup: func() { + evalSET([]string{"string_key", "string_value"}, store) + }, + input: []string{"string_key", "0"}, + output: []byte("-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"), + }, + "HSCAN with valid key and cursor": { + setup: func() { + evalHSET([]string{"hash_key", "field1", "value1", "field2", "value2"}, store) + }, + input: []string{"hash_key", "0"}, + output: []byte("*2\r\n$1\r\n0\r\n*4\r\n$6\r\nfield1\r\n$6\r\nvalue1\r\n$6\r\nfield2\r\n$6\r\nvalue2\r\n"), + }, + "HSCAN with cursor at the end": { + setup: func() { + evalHSET([]string{"hash_key", "field1", "value1", "field2", "value2"}, store) + }, + input: []string{"hash_key", "2"}, + output: []byte("*2\r\n$1\r\n0\r\n*0\r\n"), + }, + "HSCAN with cursor at the beginning": { + setup: func() { + evalHSET([]string{"hash_key", "field1", "value1", "field2", "value2"}, store) + }, + input: []string{"hash_key", "0"}, + output: []byte("*2\r\n$1\r\n0\r\n*4\r\n$6\r\nfield1\r\n$6\r\nvalue1\r\n$6\r\nfield2\r\n$6\r\nvalue2\r\n"), + }, + "HSCAN with cursor in the middle": { + setup: func() { + evalHSET([]string{"hash_key", "field1", "value1", "field2", "value2"}, store) + }, + input: []string{"hash_key", "1"}, + output: []byte("*2\r\n$1\r\n0\r\n*2\r\n$6\r\nfield2\r\n$6\r\nvalue2\r\n"), + }, + "HSCAN with MATCH argument": { + setup: func() { + evalHSET([]string{"hash_key", "field1", "value1", "field2", "value2", "field3", "value3"}, store) + }, + input: []string{"hash_key", "0", "MATCH", "field[12]*"}, + output: []byte("*2\r\n$1\r\n0\r\n*4\r\n$6\r\nfield1\r\n$6\r\nvalue1\r\n$6\r\nfield2\r\n$6\r\nvalue2\r\n"), + }, + "HSCAN with COUNT argument": { + setup: func() { + evalHSET([]string{"hash_key", "field1", "value1", "field2", "value2", "field3", "value3"}, store) + }, + input: []string{"hash_key", "0", "COUNT", "2"}, + output: []byte("*2\r\n$1\r\n2\r\n*4\r\n$6\r\nfield1\r\n$6\r\nvalue1\r\n$6\r\nfield2\r\n$6\r\nvalue2\r\n"), + }, + "HSCAN with MATCH and COUNT arguments": { + setup: func() { + evalHSET([]string{"hash_key", "field1", "value1", "field2", "value2", "field3", "value3", "field4", "value4"}, store) + }, + input: []string{"hash_key", "0", "MATCH", "field[13]*", "COUNT", "1"}, + output: []byte("*2\r\n$1\r\n1\r\n*2\r\n$6\r\nfield1\r\n$6\r\nvalue1\r\n"), + }, + "HSCAN with invalid MATCH pattern": { + setup: func() { + evalHSET([]string{"hash_key", "field1", "value1", "field2", "value2"}, store) + }, + input: []string{"hash_key", "0", "MATCH", "[invalid"}, + output: []byte("-ERR Invalid glob pattern: unexpected end of input\r\n"), + }, + "HSCAN with invalid COUNT value": { + setup: func() { + evalHSET([]string{"hash_key", "field1", "value1", "field2", "value2"}, store) + }, + input: []string{"hash_key", "0", "COUNT", "invalid"}, + output: []byte("-ERR value is not an integer or out of range\r\n"), + }, + } + + runEvalTests(t, tests, evalHSCAN, store) +} + func testEvalPFMERGE(t *testing.T, store *dstore.Store) { tests := map[string]evalTestCase{ "nil value": {input: nil, output: []byte("-ERR wrong number of arguments for 'pfmerge' command\r\n")}, diff --git a/main.go b/main.go index 5cd217b5c..a66e824ad 100644 --- a/main.go +++ b/main.go @@ -50,7 +50,7 @@ func main() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT) - queryWatchChan := make(chan dstore.QueryWatchEvent, config.DiceConfig.Server.KeysLimit) + queryWatchChan := make(chan dstore.QueryWatchEvent, config.DiceConfig.Server.WatchChanBufSize) cmdWatchChan := make(chan dstore.CmdWatchEvent, config.DiceConfig.Server.KeysLimit) var serverErrCh chan error