Alright, gather ’round everyone! Today we’re diving deep into the swirling vortex of Redis 7.x, focusing on the two titans of this release: Redis Functions and ACL v2. Buckle up, because we’re about to explore features that’ll make your Redis data sing (or at least, perform a whole lot faster and safer).
Part 1: Redis Functions – Unleash the Power Within (Your Redis Server)
For years, we’ve been shoving data into Redis and then pulling it back out to do the real work. This meant network round trips, serialization/deserialization overhead, and generally a lot of back-and-forth that could make even the speediest SSD blush. Redis Functions change all that.
Imagine you could write a small program, a tiny wizard, and embed it directly into the Redis server. This wizard could then manipulate your data right there, without ever leaving the Redis process. That’s the essence of Redis Functions.
What are Redis Functions, Exactly?
Redis Functions are server-side functions written in Lua, compiled to bytecode, and stored within Redis itself. You can then call these functions using a special command, passing in keys, arguments, and generally making them dance to your data-manipulation tune.
Why Should I Care?
- Reduced Latency: No more network trips for simple data manipulations. Everything happens in-process.
- Atomic Operations: Lua scripts execute atomically, guaranteeing data consistency. Think of them as supercharged transactions.
- Simplified Logic: Complex data transformations can be encapsulated in a single function, making your client code cleaner and more readable.
- Extensibility: Redis becomes a platform for running custom logic, tailored to your specific application needs.
- Bandwidth Savings: Less data moving across the network means more bandwidth for other important tasks (like streaming cat videos).
Hands-On: Writing Your First Redis Function
Let’s start with a simple example. We’ll create a function that increments a counter by a given amount.
-- increment.lua
local key = KEYS[1] -- The key to increment
local increment_by = tonumber(ARGV[1]) -- The amount to increment by
local current_value = redis.call('GET', key)
if not current_value then
current_value = 0
end
local new_value = tonumber(current_value) + increment_by
redis.call('SET', key, new_value)
return new_value
Let’s break this down:
KEYS[1]
: Redis Functions receive keys as a table namedKEYS
. In this case, we’re expecting one key, the one to increment.ARGV[1]
: Arguments are passed in a table calledARGV
. We need to convert the argument to a number usingtonumber()
.redis.call('GET', key)
: This is how you execute standard Redis commands from within your Lua script. It’s like having a mini-Redis client inside your function.- Error handling: If the key does not exist, the current value is nil, we can assign 0.
redis.call('SET', key, new_value)
: Sets the key to the new value.return new_value
: The function returns the new value, which will be sent back to the client.
Loading the Function into Redis
Now, we need to load this script into Redis using the FUNCTION LOAD
command:
redis-cli FUNCTION LOAD "$(cat increment.lua)"
This will return a function name that looks something like: ffa44f817202f774b2b31a040215a05a8156d507
. This is the SHA1 hash of the script, and it’s how you’ll refer to the function from now on.
Calling the Function
To call the function, use the FCALL
command:
redis-cli FCALL ffa44f817202f774b2b31a040215a05a8156d507 1 mycounter 10
Let’s dissect this:
FCALL
: The command to execute a Redis Function.ffa44f817202f774b2b31a040215a05a8156d507
: The name of the function (the SHA1 hash).1
: The number of keys the function will access.mycounter
: The key to increment.10
: The amount to increment by.
If the key mycounter
exists, it will be incremented by 10. If it doesn’t exist, it will be created and set to 10. The FCALL
command will return the new value of the counter.
A More Complex Example: Calculating a Running Average
Let’s get a bit more ambitious. This function will calculate a running average for a given key. It will store the sum of the values and the number of values in two separate Redis keys.
-- running_average.lua
local key = KEYS[1] -- The base key
local value = tonumber(ARGV[1]) -- The new value to add
local sum_key = key .. ":sum"
local count_key = key .. ":count"
local current_sum = redis.call('GET', sum_key)
if not current_sum then
current_sum = 0
end
current_sum = tonumber(current_sum)
local current_count = redis.call('GET', count_key)
if not current_count then
current_count = 0
end
current_count = tonumber(current_count)
local new_sum = current_sum + value
local new_count = current_count + 1
redis.call('SET', sum_key, new_sum)
redis.call('SET', count_key, new_count)
local average = new_sum / new_count
return average
In this example:
- We’re creating two derived keys:
key:sum
andkey:count
to store the running sum and count. - We retrieve the current sum and count from Redis (or initialize them to 0 if they don’t exist).
- We calculate the new sum and count.
- We update the sum and count keys in Redis.
- Finally, we calculate the average and return it.
Load and call this function similarly to the previous example. For instance, if the SHA1 hash is b380f731b5e81b3841f89b509d4d9916f6988d4a
:
redis-cli FCALL b380f731b5e81b3841f89b509d4d9916f6988d4a 1 mydata 5
This will add the value 5
to the running average calculation for the key mydata
.
Function Libraries
Redis 7.x also introduces the concept of function libraries. You can group related functions into a library, which makes it easier to manage and organize your server-side code.
To create a library, use the FUNCTION LOAD
command with the LIBRARY
option:
redis-cli FUNCTION LOAD LIBRARY "mylibrary" "$(cat myfunctions.lua)"
Inside myfunctions.lua
, you’ll define your functions using the redis.register_function()
API:
-- myfunctions.lua
local function increment(keys, args)
local key = keys[1]
local increment_by = tonumber(args[1])
local current_value = redis.call('GET', key)
if not current_value then
current_value = 0
end
local new_value = tonumber(current_value) + increment_by
redis.call('SET', key, new_value)
return new_value
end
redis.register_function('increment', increment)
To call a function from a library, use the FCALL
command with the library name and function name:
redis-cli FCALL mylibrary.increment 1 mycounter 10
Important Considerations
- Resource Limits: Be mindful of the resources your functions consume. Long-running or computationally intensive functions can impact Redis performance. Use the
redis.log()
function to help you measure the resource consumption of your functions. - Debugging: Debugging Lua scripts inside Redis can be tricky. Use
redis.log()
liberally to print debugging information to the Redis server log. - Security: Be careful about the code you load into Redis. Malicious code can compromise your entire Redis instance. Only load functions from trusted sources.
Summary of Redis Functions Commands
Command | Description |
---|---|
FUNCTION LOAD |
Loads a function or library into Redis. |
FUNCTION DELETE |
Deletes a function or library. |
FUNCTION LIST |
Lists all loaded functions and libraries. |
FUNCTION DUMP |
Returns the serialized representation of a function. |
FUNCTION RESTORE |
Restores a function from its serialized representation. |
FCALL |
Executes a function. |
Part 2: ACL v2 – Fort Knox for Your Redis Data
Access Control Lists (ACLs) are your first line of defense against unauthorized access to your Redis data. ACL v2 in Redis 7.x takes security to a whole new level, offering granular control over who can do what with your data.
What’s New in ACL v2?
- Command Categories: Permissions can now be granted based on command categories, not just individual commands. This simplifies ACL management and makes it easier to enforce security policies.
- Improved Syntax: The ACL syntax is more intuitive and easier to read.
- Dynamic Users: ACLs can be applied to dynamic users, making it easier to manage access for applications that create and destroy users on the fly.
- More Granular Key Permissions: Key permissions can now be specified using glob-style patterns, allowing you to control access to specific sets of keys.
- Pub/Sub Channel Permissions: Fine-grained control over which channels users can subscribe to or publish on.
Why Should I Care?
- Enhanced Security: Protect your sensitive data from unauthorized access.
- Compliance: Meet regulatory requirements for data security and privacy.
- Multi-Tenancy: Securely isolate data for different tenants in a shared Redis environment.
- Defense in Depth: Add an extra layer of security to your Redis deployment.
Hands-On: Configuring ACL v2
Let’s create a user with limited access to our Redis data.
Creating a User
To create a user, use the ACL SETUSER
command:
redis-cli ACL SETUSER myuser
This creates a user named myuser
with no permissions. They can’t do anything yet.
Setting Permissions
Now, let’s give myuser
permission to read and write keys that start with myprefix:
.
redis-cli ACL SETUSER myuser +@read +@write ~myprefix:*
Let’s break down this command:
ACL SETUSER myuser
: Specifies the user we’re modifying.+@read
: Grants the user permission to execute commands in the@read
category (e.g.,GET
,HGET
,SMEMBERS
).+@write
: Grants the user permission to execute commands in the@write
category (e.g.,SET
,HSET
,SADD
).~myprefix:*
: Grants the user permission to access keys that match the patternmyprefix:*
. The~
indicates a key pattern.
Setting a Password
To set a password for the user, use the ACL SETUSER
command with the >
option:
redis-cli ACL SETUSER myuser >mypassword
Now, myuser
can only connect to Redis using the password mypassword
.
Connecting as the User
To connect as myuser
using redis-cli
, use the -u
and -a
options:
redis-cli -u myuser -a mypassword
If you try to access a key that doesn’t match the myprefix:*
pattern, or execute a command outside the @read
and @write
categories, you’ll get a permission error.
Denying Permissions
To deny a user permission, use the -
option:
redis-cli ACL SETUSER myuser -@write
This revokes the user’s @write
permissions.
Using Command Categories
Redis 7.x introduces command categories, which are groups of related commands. Here are some common categories:
Category | Description | Examples |
---|---|---|
@admin |
Administrative commands (dangerous). | CONFIG , DEBUG , FLUSHALL |
@read |
Read-only commands. | GET , HGET , SMEMBERS , LLEN |
@write |
Write commands. | SET , HSET , SADD , LPUSH |
@slow |
Commands that can be slow. | KEYS , SMEMBERS (on large sets) |
@pubsub |
Pub/Sub related commands. | PUBLISH , SUBSCRIBE , PSUBSCRIBE |
@blocking |
Blocking commands. | BLPOP , BRPOP , BRPOPLPUSH |
@dangerous |
Commands that can have unintended consequences. | RENAME , DEL |
You can grant or deny permissions to entire categories, making it easier to manage complex ACL configurations.
Pub/Sub Channel Permissions
ACL v2 allows you to control which channels users can subscribe to or publish on.
To grant a user permission to subscribe to channels that match a pattern, use the +PUBSUB
option:
redis-cli ACL SETUSER myuser +PUBSUB mychannel:*
This allows myuser
to subscribe to channels that start with mychannel:
.
To grant a user permission to publish to channels that match a pattern, use the +PUBSUB
option with the >
prefix:
redis-cli ACL SETUSER myuser +PUBSUB >mychannel:*
This allows myuser
to publish to channels that start with mychannel:
.
Important Considerations
- Default User: The default user (the user with no username or password) has full access to Redis. Disable or restrict this user in production environments.
- ACL Log: Redis logs ACL events to the server log. Monitor the log for suspicious activity.
- Testing: Thoroughly test your ACL configuration to ensure that users have the correct permissions.
- Complexity: ACLs can become complex. Document your ACL configuration carefully.
Summary of ACL Commands
Command | Description |
---|---|
ACL SETUSER |
Creates or modifies a user. |
ACL DELUSER |
Deletes a user. |
ACL GETUSER |
Retrieves information about a user. |
ACL LIST |
Lists all users. |
ACL WHOAMI |
Returns the username of the current connection. |
ACL CAT |
Lists command categories. |
ACL LOG |
Shows recent ACL security events. |
ACL GENPASS |
Generates a secure password. |
ACL HELP |
Displays help information about ACL commands. |
Putting It All Together: A Secure and Efficient Application
Imagine you’re building a real-time analytics application. You want to store event data in Redis and calculate some running statistics. You can combine Redis Functions and ACL v2 to create a secure and efficient solution.
- Create a Redis Function: Write a function that receives event data and updates the running statistics (e.g., using the running average example from earlier).
- Load the Function into Redis: Load the function into Redis using
FUNCTION LOAD
. - Create an ACL User: Create an ACL user for your application that has permission to execute the Redis function and access the relevant keys.
- Configure the Application: Configure your application to connect to Redis as the ACL user and call the Redis function to process event data.
This approach provides several benefits:
- Security: The ACL user limits the application’s access to Redis, preventing it from performing unauthorized operations.
- Efficiency: The Redis function performs the data processing in-process, reducing latency and network traffic.
- Maintainability: The data processing logic is encapsulated in a single Redis function, making it easier to maintain and update.
Conclusion
Redis Functions and ACL v2 are powerful tools that can significantly improve the performance, security, and manageability of your Redis deployments. By understanding these features and incorporating them into your applications, you can unlock the full potential of Redis. Now go forth and build amazing things! Remember to always err on the side of caution when dealing with security, and never, ever, hardcode passwords. Happy coding!