Redis Conditional Update in Node.js (Detailed Guide w/ Code Examples)
Use Case(s)
- Implementing optimistic locking to prevent race conditions.
- Updating a value in the cache only if it meets specific criteria.
- Maintaining consistency between the cache and a backing store.
Code Examples
Example 1: Conditional Update Using Watch and Multi
This example demonstrates how to use the watch
and multi
commands to perform a conditional update. The value is updated only if it has not been modified by another client during the transaction.
const redis = require('redis'); const client = redis.createClient(); client.on('error', (err) => { console.error('Error:', err); }); const key = 'user:1000'; const newValue = { score: 200 }; client.watch(key, (watchErr) => { if (watchErr) throw watchErr; client.get(key, (getErr, result) => { if (getErr) throw getErr; const parsedResult = JSON.parse(result); if (parsedResult.score < newValue.score) { const multi = client.multi(); multi.set(key, JSON.stringify(newValue)); multi.exec((execErr, replies) => { if (execErr) throw execErr; if (replies === null) { console.log('Transaction aborted due to concurrent modification.'); } else { console.log('Transaction successful:', replies); } client.quit(); }); } else { console.log('Condition not met, no update performed.'); client.quit(); } }); });
Explanation:
watch(key)
: Starts watching the key for changes.get(key)
: Retrieves the current value of the key.- Condition check
if (parsedResult.score < newValue.score)
: Ensures that the new value is only set if the existing score is lower than the new score. multi()
: Initiates a transaction.exec()
: Executes the transaction if the key hasn't been modified sincewatch()
.
Example 2: Conditional Increment with Lua Script
Using a Lua script ensures atomic execution of the conditional operation.
const redis = require('redis'); const client = redis.createClient(); client.on('error', (err) => { console.error('Error:', err); }); const key = 'counter:1000'; const incrementBy = 10; const condition = 50; // Only increment if current value is less than this const script = ` local current = tonumber(redis.call('get', KEYS[1])) if current < tonumber(ARGV[1]) then return redis.call('incrby', KEYS[1], ARGV[2]) else return current end `; client.eval(script, 1, key, condition, incrementBy, (err, result) => { if (err) throw err; console.log('Result:', result); client.quit(); });
Explanation:
eval(script, 1, key, condition, incrementBy)
: Runs a Lua script atomically.- The Lua script checks if the current value is less than a specified condition before performing an increment.
Best Practices
- Use Lua scripts for atomic operations that involve multiple commands or complex logic.
- Implement proper error handling to handle cases where transactions are aborted due to concurrent modifications.
Common Mistakes
- Forgetting to call
watch()
before starting a transaction can lead to lost updates. - Not checking the result of
exec()
for a null value, which indicates that the transaction was aborted.
FAQs
Q: What happens if the watched key is modified by another client before exec()
?
A: The transaction will fail (exec()
returns null), and you should retry the operation.
Q: Why use Lua scripts for conditional updates? A: Lua scripts ensure atomicity and can perform more complex logic than simple Redis commands, reducing the risk of race conditions.
Was this content helpful?
Similar Code Examples
Switch & save up to 80%
Dragonfly is fully compatible with the Redis ecosystem and requires no code changes to implement. Instantly experience up to a 25X boost in performance and 80% reduction in cost