In node project, we often run some time task, such as regularly send email notification, timing, etc., project deployment, we tend to be more machine instance deployed, resulting in each instance will run the same task at a time, so we need a distributed transaction locks, to ensure that the task can only be run once.

Distributed transaction lock

Distributed transaction locks can be implemented in many ways, which can be roughly divided into the following categories

  • Database based implementation
  • Cache-based (Redis, memcached)
  • Based on a Zookeeper

According to current requirements, it is too troublesome to use database lock. Zookeeper has not been used in production at present, but it happens to be used in redis project, so we adopt the second implementation method

redLock

Redis officially recommended the corresponding solution Redlock, the official list of each language implementation, including node implementation, as shown below

RedLock – node implementation

The library is packaged and very simple to use

The Configuration Configuration

var client1 = require('redis').createClient(6379, 'redis1.example.com');
var client2 = require('redis').createClient(6379, 'redis2.example.com');
var client3 = require('redis').createClient(6379, 'redis3.example.com');
var Redlock = require('redlock');

var redlock = new Redlock(
	// you should have one client for each independent redis node
	// or cluster
	[client1, client2, client3],
	{
		// the expected clock drift; forMore details / / see http://redis.io/topics/distlock driftFactor: 0.01, / / timein ms

		// the max number of times Redlock will attempt
		// to lock a resource before erroring
		retryCount:  10,

		// the time in ms between attempts
		retryDelay:  200, // time in ms

		// the max time in ms randomly added to retries
		// to improve performance under high contention
		// see https://www.awsarchitectureblog.com/2015/03/backoff.html
		retryJitter:  200 // time in ms
	}
);
Copy the code

Locking & Unlocking Transactions and Unlocking locks

// the string identifier for the resource you want to lock
var resource = 'locks:account:322456';

// the maximum amount of time you want the resource locked,
// keeping inmind that you can extend the lock up until // the point when it expires var ttl = 1000; Redlock. lock(resource, TTL).then(function(lock) { // ... do something here... // unlock your resource when you aredone
	return lock.unlock()
	.catch(function(err) {
		// we weren't able to reach redis; your lock will eventually // expire, but you probably want to log this error console.error(err); }); })Copy the code

With the above approach, we can implement distributed transaction locking

Problems encountered

In the testing process, found that the transaction is not locked, check, found two computer system time is not consistent, the difference of 10 seconds (test partner for other tasks manually adjust the time), as a result, time machine to run the task first, early time slow machines, when to get the lock, lock have been released, So RedLock builds on the model that Time is trusted. Redis RedLock is the perfect distributed lock? Very well explained

Read a sentence in another blog

The distributed CAP theory tells us that “no distributed system can satisfy Consistency, Availability and Partition tolerance at most.”

A good but imperfect solution coupled with good operations will solve most of the business requirements.

About timeout TTL set has a problem, how long is set is better, if the setting is too short, didn’t performed the task, the lock is released, on the contrary, if the setting time is too long, the other thread fetching the lock may have to wait for a period of time more straightforward, so this will be set according to specific business scenarios

summary

If redis is using a single machine, there is no need to use the redlock scheme, directly use setnx to set a flag bit, ok