Skip to content

Commit 7efd752

Browse files
author
Alexander Dmitrjuk
committed
Redis Balance anything
1 parent 689ee8f commit 7efd752

File tree

6 files changed

+110
-187
lines changed

6 files changed

+110
-187
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Redis functions balancer
22
[![NPM](https://nodei.co/npm/redis-functions-balancer.png)](https://nodei.co/npm/redis-functions-balancer/)
33

4-
Balance executes of NodeJs-functions with redis.
4+
Balance executes of NodeJs-functions or anything with redis.
55

66
For example, if you have several functions (A, B, C) doing the same things (http requests, long-running code), and you want to execute it evenly.
77

@@ -16,9 +16,9 @@ Ready to use with TypeScript and JavaScript.
1616
npm install redis-functions-balancer --save-prod
1717
```
1818

19-
## Usage
19+
## Example of usage
2020
```typescript
21-
import RedisFunctionsBalancer from "redis-functions-balancer";
21+
import RedisBalancer from "redis-functions-balancer";
2222
const redis = require("redis");
2323
const redisClient = redis.createClient(6379, 'redis');
2424

@@ -28,9 +28,9 @@ const A = () => {};
2828
const B = () => {};
2929
const C = () => {};
3030
// ... //
31-
let balancer = new RedisFunctionsBalancer([A, B, C], redisClient);
31+
let balancer = new RedisBalancer([A, B, C], redisClient);
3232
// or reuse balancer variable with another functions
33-
balancer.setMethods([A, B]);
33+
balancer.setData([A, B]);
3434
// ... //
3535
// Get async iterator {done, value}
3636
let iterator = await balancer.getAsyncIterator();

index.js

Lines changed: 63 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
88
step((generator = generator.apply(thisArg, _arguments || [])).next());
99
});
1010
};
11-
var __generator = (this && this.__generator) || function (thisArg, body) {
12-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14-
function verb(n) { return function (v) { return step([n, v]); }; }
15-
function step(op) {
16-
if (f) throw new TypeError("Generator is already executing.");
17-
while (_) try {
18-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19-
if (y = 0, t) op = [op[0] & 2, t.value];
20-
switch (op[0]) {
21-
case 0: case 1: t = op; break;
22-
case 4: _.label++; return { value: op[1], done: false };
23-
case 5: _.label++; y = op[1]; op = [0]; continue;
24-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
25-
default:
26-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30-
if (t[2]) _.ops.pop();
31-
_.trys.pop(); continue;
32-
}
33-
op = body.call(thisArg, _);
34-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36-
}
37-
};
3811
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
3912
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
4013
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
@@ -47,27 +20,20 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
4720
function reject(value) { resume("throw", value); }
4821
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
4922
};
50-
var __spreadArrays = (this && this.__spreadArrays) || function () {
51-
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
52-
for (var r = Array(s), k = 0, i = 0; i < il; i++)
53-
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
54-
r[k] = a[j];
55-
return r;
56-
};
5723
Object.defineProperty(exports, "__esModule", { value: true });
58-
var util_1 = require("util");
59-
var RedisFunctionsBalancer = /** @class */ (function () {
24+
const util_1 = require("util");
25+
class RedisBalancer {
6026
/**
6127
*
62-
* @param methods not empty array of functions
28+
* @param data not empty array of functions
6329
* @param redisClient
6430
*/
65-
function RedisFunctionsBalancer(methods, redisClient) {
31+
constructor(data, redisClient) {
6632
this._STORE_PREFIX = 'balancer';
6733
this.INC_VALUE = 1;
6834
this._redisClient = redisClient;
69-
this._methods = methods;
70-
this._storeKey = this.makeStoreKey(methods);
35+
this._data = data;
36+
this._storeKey = this.makeStoreKey(data);
7137
// Initialize Redis functions as async await
7238
this._functions = {
7339
delAsync: util_1.promisify(redisClient.DEL).bind(this._redisClient),
@@ -76,120 +42,75 @@ var RedisFunctionsBalancer = /** @class */ (function () {
7642
zIncRbyAsync: util_1.promisify(redisClient.zincrby).bind(this._redisClient),
7743
};
7844
}
79-
RedisFunctionsBalancer.prototype.setMethods = function (methods) {
80-
this._methods = methods;
81-
this._storeKey = this.makeStoreKey(methods);
82-
};
83-
RedisFunctionsBalancer.prototype.increaseRank = function (func, incValue) {
84-
if (incValue === void 0) { incValue = this.INC_VALUE; }
85-
return __awaiter(this, void 0, void 0, function () {
86-
return __generator(this, function (_a) {
87-
switch (_a.label) {
88-
case 0: return [4 /*yield*/, this._functions.zIncRbyAsync(this._storeKey, incValue, func.name)];
89-
case 1:
90-
_a.sent();
91-
return [2 /*return*/];
92-
}
93-
});
45+
setData(data) {
46+
this._data = data;
47+
this._storeKey = this.makeStoreKey(data);
48+
}
49+
increaseRank(record, incValue = this.INC_VALUE) {
50+
return __awaiter(this, void 0, void 0, function* () {
51+
let key = this._data.indexOf(record);
52+
return this.increaseRankByIndex(key, incValue);
9453
});
95-
};
96-
RedisFunctionsBalancer.prototype.getAsyncIterator = function () {
97-
return __asyncGenerator(this, arguments, function getAsyncIterator_1() {
98-
var storedMethodNames, _i, storedMethodNames_1, methodName, _a, _b, method;
99-
return __generator(this, function (_c) {
100-
switch (_c.label) {
101-
case 0: return [4 /*yield*/, __await(this.getRange())];
102-
case 1:
103-
storedMethodNames = _c.sent();
104-
_i = 0, storedMethodNames_1 = storedMethodNames;
105-
_c.label = 2;
106-
case 2:
107-
if (!(_i < storedMethodNames_1.length)) return [3 /*break*/, 9];
108-
methodName = storedMethodNames_1[_i];
109-
_a = 0, _b = this._methods;
110-
_c.label = 3;
111-
case 3:
112-
if (!(_a < _b.length)) return [3 /*break*/, 8];
113-
method = _b[_a];
114-
if (!(method.name === methodName)) return [3 /*break*/, 7];
115-
return [4 /*yield*/, __await(this.increaseRank(method, this.INC_VALUE))];
116-
case 4:
117-
_c.sent();
118-
return [4 /*yield*/, __await(method)];
119-
case 5: return [4 /*yield*/, _c.sent()];
120-
case 6:
121-
_c.sent();
122-
_c.label = 7;
123-
case 7:
124-
_a++;
125-
return [3 /*break*/, 3];
126-
case 8:
127-
_i++;
128-
return [3 /*break*/, 2];
129-
case 9: return [2 /*return*/];
130-
}
131-
});
54+
}
55+
increaseRankByIndex(index, incValue = this.INC_VALUE) {
56+
return __awaiter(this, void 0, void 0, function* () {
57+
yield this._functions.zIncRbyAsync(this._storeKey, incValue, index.toString());
13258
});
133-
};
134-
/**
135-
* Clear store
136-
*/
137-
RedisFunctionsBalancer.prototype.resetStore = function () {
138-
return __awaiter(this, void 0, void 0, function () {
139-
return __generator(this, function (_a) {
140-
switch (_a.label) {
141-
case 0: return [4 /*yield*/, this._functions.delAsync(this._storeKey)];
142-
case 1:
143-
_a.sent();
144-
return [2 /*return*/];
59+
}
60+
getAsyncIterator() {
61+
return __asyncGenerator(this, arguments, function* getAsyncIterator_1() {
62+
let storedData = yield __await(this.getRange());
63+
// Redis store defined
64+
for (let storedKey of storedData) {
65+
for (let [key, record] of this._data.entries()) {
66+
if (storedKey === key.toString()) {
67+
yield __await(this.increaseRankByIndex(key, this.INC_VALUE));
68+
yield yield __await(record);
69+
}
14570
}
146-
});
71+
}
14772
});
148-
};
149-
RedisFunctionsBalancer.prototype.getStoreKey = function () {
73+
}
74+
resetStore() {
75+
return __awaiter(this, void 0, void 0, function* () {
76+
yield this._functions.delAsync(this._storeKey);
77+
});
78+
}
79+
getStoreKey() {
15080
return this._storeKey;
151-
};
81+
}
15282
/**
153-
* Return redis key to store list of methods with ranks
154-
* @param methods
83+
* Return redis key to store list of data with ranks
84+
* @param data
15585
* @protected
15686
*/
157-
RedisFunctionsBalancer.prototype.makeStoreKey = function (methods) {
158-
var storeKeyArray = [this._STORE_PREFIX];
159-
methods.forEach(function (method) {
160-
storeKeyArray.push(method.name);
87+
makeStoreKey(data) {
88+
let storeKeyArray = [this._STORE_PREFIX];
89+
data.forEach((method, index) => {
90+
storeKeyArray.push(index.toString());
16191
});
16292
return storeKeyArray.join('.');
163-
};
93+
}
16494
/**
16595
* Returns an Array stored in Redis in Rank order
16696
* @private
16797
*/
168-
RedisFunctionsBalancer.prototype.getRange = function () {
169-
return __awaiter(this, void 0, void 0, function () {
170-
var storedMethodNames, args_1, result_1;
171-
var _a;
172-
return __generator(this, function (_b) {
173-
switch (_b.label) {
174-
case 0: return [4 /*yield*/, this._functions.zRangeAsync(this._storeKey, 0, -1)];
175-
case 1:
176-
storedMethodNames = _b.sent();
177-
if (!(storedMethodNames.length !== this._methods.length)) return [3 /*break*/, 3];
178-
args_1 = [], result_1 = [];
179-
this._methods.forEach(function (method) {
180-
// Default rank is 1
181-
args_1.push("1", method.name);
182-
result_1.push(method.name);
183-
});
184-
return [4 /*yield*/, (_a = this._functions).zAddAsync.apply(_a, __spreadArrays([this._storeKey, 'NX'], args_1))];
185-
case 2:
186-
_b.sent();
187-
return [2 /*return*/, result_1];
188-
case 3: return [2 /*return*/, storedMethodNames];
189-
}
190-
});
98+
getRange() {
99+
return __awaiter(this, void 0, void 0, function* () {
100+
let storedMethodNames = yield this._functions.zRangeAsync(this._storeKey, 0, -1);
101+
// If Redis store is not initialized yield in default order
102+
if (storedMethodNames.length !== this._data.length) {
103+
let args = [], result = [];
104+
this._data.forEach((record, index) => {
105+
// Default rank is 1
106+
args.push("1", index.toString());
107+
result.push(index.toString());
108+
});
109+
yield this._functions.zAddAsync(this._storeKey, 'NX', ...args);
110+
return result;
111+
}
112+
return storedMethodNames;
191113
});
192-
};
193-
return RedisFunctionsBalancer;
194-
}());
195-
exports.default = RedisFunctionsBalancer;
114+
}
115+
}
116+
exports.default = RedisBalancer;

index.ts

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ type RedisFunctions = {
88
zIncRbyAsync: (key: string, incValue: number, element: string) => Promise<string>;
99
};
1010

11-
export default class RedisFunctionsBalancer<T extends Function> {
11+
export default class RedisBalancer<T> {
1212
private _storeKey: string;
13-
private _methods: Array<T>;
13+
private _data: Array<T>;
1414
private readonly _STORE_PREFIX = 'balancer';
1515
private readonly _redisClient: RedisClient;
1616
private readonly INC_VALUE = 1;
@@ -19,13 +19,13 @@ export default class RedisFunctionsBalancer<T extends Function> {
1919

2020
/**
2121
*
22-
* @param methods not empty array of functions
22+
* @param data not empty array of functions
2323
* @param redisClient
2424
*/
25-
constructor(methods: Array<T>, redisClient: RedisClient) {
25+
constructor(data: Array<T>, redisClient: RedisClient) {
2626
this._redisClient = redisClient;
27-
this._methods = methods;
28-
this._storeKey = this.makeStoreKey(methods);
27+
this._data = data;
28+
this._storeKey = this.makeStoreKey(data);
2929

3030
// Initialize Redis functions as async await
3131
this._functions = {
@@ -36,32 +36,34 @@ export default class RedisFunctionsBalancer<T extends Function> {
3636
};
3737
}
3838

39-
public setMethods(methods: Array<T>) {
40-
this._methods = methods;
41-
this._storeKey = this.makeStoreKey(methods);
39+
public setData(data: Array<T>) {
40+
this._data = data;
41+
this._storeKey = this.makeStoreKey(data);
4242
}
4343

44-
public async increaseRank(func: T, incValue: number = this.INC_VALUE) {
45-
await this._functions.zIncRbyAsync(this._storeKey, incValue, func.name);
44+
public async increaseRank(record: T, incValue: number = this.INC_VALUE) {
45+
let key = this._data.indexOf(record);
46+
return this.increaseRankByIndex(key, incValue)
47+
}
48+
49+
protected async increaseRankByIndex(index: number, incValue: number = this.INC_VALUE) {
50+
await this._functions.zIncRbyAsync(this._storeKey, incValue, index.toString());
4651
}
4752

4853
public async* getAsyncIterator(): AsyncIterableIterator<T> {
49-
let storedMethodNames = await this.getRange();
54+
let storedData = await this.getRange();
5055

5156
// Redis store defined
52-
for (let methodName of storedMethodNames) {
53-
for (let method of this._methods) {
54-
if (method.name === methodName) {
55-
await this.increaseRank(method, this.INC_VALUE);
56-
yield method;
57+
for (let storedKey of storedData) {
58+
for (let [key, record] of this._data.entries()) {
59+
if (storedKey === key.toString()) {
60+
await this.increaseRankByIndex(key, this.INC_VALUE);
61+
yield record;
5762
}
5863
}
5964
}
6065
}
6166

62-
/**
63-
* Clear store
64-
*/
6567
public async resetStore(): Promise<void> {
6668
await this._functions.delAsync(this._storeKey);
6769
}
@@ -71,14 +73,14 @@ export default class RedisFunctionsBalancer<T extends Function> {
7173
}
7274

7375
/**
74-
* Return redis key to store list of methods with ranks
75-
* @param methods
76+
* Return redis key to store list of data with ranks
77+
* @param data
7678
* @protected
7779
*/
78-
protected makeStoreKey(methods: Array<T>): string {
80+
protected makeStoreKey(data: Array<T>): string {
7981
let storeKeyArray: Array<string> = [this._STORE_PREFIX];
80-
methods.forEach((method: T) => {
81-
storeKeyArray.push(method.name);
82+
data.forEach((method: T, index: number) => {
83+
storeKeyArray.push(index.toString());
8284
});
8385

8486
return storeKeyArray.join('.');
@@ -91,14 +93,14 @@ export default class RedisFunctionsBalancer<T extends Function> {
9193
protected async getRange(): Promise<Array<string>> {
9294
let storedMethodNames = await this._functions.zRangeAsync(this._storeKey, 0, -1) as Array<string>;
9395
// If Redis store is not initialized yield in default order
94-
if (storedMethodNames.length !== this._methods.length) {
96+
if (storedMethodNames.length !== this._data.length) {
9597
let args: Array<string> = [],
9698
result: Array<string> = [];
9799

98-
this._methods.forEach(method => {
100+
this._data.forEach((record, index) => {
99101
// Default rank is 1
100-
args.push("1", method.name);
101-
result.push(method.name);
102+
args.push("1", index.toString());
103+
result.push(index.toString());
102104
});
103105
await this._functions.zAddAsync(this._storeKey, 'NX', ...args);
104106

0 commit comments

Comments
 (0)