This repository was archived by the owner on Jul 11, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
94 lines (79 loc) · 1.99 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
const { EventEmitter } = require('events');
const { inherits } = require('util');
inherits(CircuitBreaker, EventEmitter);
function CircuitBreaker(command, opts = {}) {
if (!command) {
throw new Error('First argument of CircuitBreaker must be a function');
}
if (!(this instanceof CircuitBreaker)) {
return new CircuitBreaker(command, opts);
}
EventEmitter.call(this);
const {
retry = 10000,
timeout = 0,
maxError = 10,
maxTime = 1000,
fallback = () => Promise.reject(new Error('Service Currently unavailable')),
} = opts;
Object.assign(this, {
timeout: {
enabled: Boolean(timeout),
value: timeout,
},
maxError,
maxTime,
retry,
command,
fallback,
});
this.state = { isOpen: false, isHalfOpen: false, errorCount: 0 };
// Reset error count after `maxTime` ms
setInterval(() => {
this.state.errorCount = 0;
}, maxTime);
// Reset half open state every `retry` ms
setInterval(() => {
if (this.state.isOpen) {
this.state.isHalfOpen = true;
}
}, this.retry);
}
function tryCommand(...args) {
const task = this.timeout.enabled ?
Promise.race([timeoutProcess(this.timeout.value), this.command(...args)]) :
this.command(...args);
return task
.then(val => {
if (this.state.isHalfOpen) {
this.state.isHalfOpen = false;
this.state.isOpen = false;
this.emit('closed');
}
return val;
}, err => {
this.state.errorCount += 1;
if (this.state.isHalfOpen) {
this.state.isHalfOpen = false;
}
if (this.state.errorCount >= this.maxError) {
this.state.isOpen = true;
this.emit('open');
}
return Promise.reject(err);
});
}
function timeoutProcess(time) {
return new Promise(
(resolve, reject) =>
setTimeout(() => reject(new Error('Service Timed Out')), time)
);
}
function fire(...args) {
if (this.state.isOpen && !this.state.isHalfOpen) {
return this.fallback(...args);
}
return this.tryCommand(...args);
}
Object.assign(CircuitBreaker.prototype, { fire, timeoutProcess, tryCommand });
module.exports = CircuitBreaker;