Skip to content

Commit d49dd80

Browse files
authored
Merge pull request #77 from fed135/next
[Release] v1.13.0
2 parents 8b7a6b7 + b7b4fe7 commit d49dd80

File tree

16 files changed

+147
-361
lines changed

16 files changed

+147
-361
lines changed

README.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Want to make your app faster and don't want to spend on extra infrastructure ? [
2121
**HA-store** is a generic wrapper for your data queries, it features:
2222

2323
- Smart micro-caching for 'hot' information (in-memory or using the [redis-adapter](https://github.com/fed135/ha-redis-adapter))
24-
- Request coalescing, batching, retrying and circuit-breaking
24+
- Request coalescing, batching and retrying
2525
- Insightful stats and [events](#Monitoring-and-events)
2626
- Lightweight, configurable and has **zero dependencies**
2727

@@ -60,8 +60,7 @@ timeout | false | `null` | The maximum time allowed for the resolver to resolve.
6060
cache | false | <pre>{&#13;&#10;&nbsp;&nbsp;base: 1000,&#13;&#10;&nbsp;&nbsp;step: 5,&#13;&#10;&nbsp;&nbsp;limit: 30000,&#13;&#10;&nbsp;&nbsp;curve: <function(progress, start, end)>&#13;&#10;}</pre> | Caching options for the data
6161
batch | false | <pre>{&#13;&#10;&nbsp;&nbsp;tick: 50,&#13;&#10;&nbsp;&nbsp;max: 100&#13;&#10;}</pre> | Batching options for the requests
6262
retry | false | <pre>{&#13;&#10;&nbsp;&nbsp;base: 5,&#13;&#10;&nbsp;&nbsp;step: 3,&#13;&#10;&nbsp;&nbsp;limit: 5000,&#13;&#10;&nbsp;&nbsp;curve: <function(progress, start, end)>&#13;&#10;}</pre> | Retry options for the requests
63-
breaker | false | <pre>{&#13;&#10;&nbsp;&nbsp;base: 1000,&#13;&#10;&nbsp;&nbsp;step: 10,&#13;&#10;&nbsp;&nbsp;limit: 65535,&#13;&#10;&nbsp;&nbsp;curve: <function(progress, start, end)>,&#13;&#10;&nbsp;&nbsp;tolerance: 1,&#13;&#10;&nbsp;&nbsp;toleranceFrame: 10000&#13;&#10;}</pre> | Circuit-breaker options, enabled by default and triggers after the retry limit
64-
storeOptions | false | <pre>{&#13;&#10;&nbsp;&nbsp;pluginFallback: true,&#13;&#10;&nbsp;&nbsp;pluginRecoveryDelay: 10000,&#13;&#10;&nbsp;&nbsp;recordLimit: Infinity&#13;&#10;}</pre> | If the store plugin errors and `pluginFallback` is true, the Store instance will attempt to fallback to the default in-memory store. It will then attempt to recover the original store every `storePluginRecoveryDelay`.
63+
storeOptions | false | <pre>{&#13;&#10;&nbsp;&nbsp;pluginFallback: true,&#13;&#10;&nbsp;&nbsp;pluginRecoveryDelay: 10000,&#13;&#10;&nbsp;&nbsp;recordLimit: Infinity,&#13;&#10;&nbsp;&nbsp;dropFactor: 1,&#13;&#10;&nbsp;&nbsp;scavengeCycle: 50&#13;&#10;}</pre> | If the store plugin errors and `pluginFallback` is true, the Store instance will attempt to fallback to the default in-memory store. It will then attempt to recover the original store every `storePluginRecoveryDelay`. `dropFactor` is the tuning element for the algorithm that marks records as relevant or not. A higher value (>1) means a more agressive marker, while a lower value (<1) makes it more allowing. `scavengeCycle` is the delay in ms between GC cycles for the store.
6564

6665
*All options are in (ms)
6766
*Scaling options are represented via and exponential curve with base and limit being the 2 edge values while steps is the number of events over that curve.
@@ -74,17 +73,14 @@ Event | Description
7473
--- | ---
7574
cacheHit | When the requested item is present in the microcache, or is already being fetched. Prevents another request from being created.
7675
cacheMiss | When the requested item is not present in the microcache and is not currently being fetched. A new request will be made.
77-
cacheFull | Whenever a store set is denied because the maximum number of records was reached for that store.
76+
cacheSkip | Whenever a store set is denied because the maximum number of records was reached for that store, or it was marked as extraneous.
7877
coalescedHit | When a record query successfully hooks to the promise of the same record in transit.
7978
query | When a batch of requests is about to be sent.
8079
queryFailed | Indicates that the batch has failed. Retry policy will dictate if it should be re-attempted.
8180
retryCancelled | Indicates that the batch has reached the allowed number of retries and is now abandoning.
8281
querySuccess | Indicates that the batch request was successful.
8382
bumpCache | When a call for an item fully loaded in the microcache succeeds, its ttl gets extended.
8483
clearCache | When an item in the microcache has reached its ttl and is now being evicted.
85-
circuitBroken | When a batch call fails after the limit amount of retries, the circuit gets broken - all calls in the next ttl will automatically fail. It is assumed that there is a problem with the data-source.
86-
circuitRestored | Circuit temporarily restored, a tentative to the data-source may be sent.
87-
circuitRecovered | The tentative request was successful and the wrapper assumes that the data-source has recovered.
8884
storePluginErrored | The custom store has encountered an error
8985
storePluginRestored | The custom store has been re-instantiated
9086

package.json

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ha-store",
3-
"version": "1.12.1",
3+
"version": "1.13.0",
44
"description": "Efficient data fetching",
55
"main": "src/index.js",
66
"scripts": {
@@ -18,26 +18,18 @@
1818
},
1919
"keywords": [
2020
"store",
21-
"javascript",
2221
"high",
2322
"availability",
2423
"network",
25-
"node",
2624
"optimize",
2725
"throughput",
2826
"retry",
29-
"circuit",
30-
"breaker",
3127
"request",
3228
"cache",
3329
"service",
3430
"batch",
3531
"micro",
3632
"latency",
37-
"messaging",
38-
"queue",
39-
"web",
40-
"entity",
4133
"congestion",
4234
"control"
4335
],
@@ -48,6 +40,7 @@
4840
"license": "Apache-2.0",
4941
"devDependencies": {
5042
"chai": "^4.2.0",
43+
"heapdump": "^0.3.12",
5144
"mocha": "^6.0.0",
5245
"sinon": "^7.2.0"
5346
},

src/breaker.js

Lines changed: 0 additions & 91 deletions
This file was deleted.

src/index.d.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,6 @@ type GenericCurveConfig = {
77
curve (progress: number, start: number, end: number): number
88
}
99

10-
type BreakerCurveConfig = {
11-
base: number
12-
steps: number
13-
limit: number
14-
curve (progress: number, start: number, end: number): number
15-
tolerance: number
16-
toleranceFrame: number
17-
}
18-
1910
type Params = {
2011
[key: string]: string
2112
}
@@ -36,13 +27,13 @@ declare interface BatcherConfig {
3627
max: number
3728
}
3829
retry?: GenericCurveConfig
39-
breaker?: GenericCurveConfig
4030
store?: any
4131
storeOptions?: {
4232
pluginFallback?: boolean
4333
pluginRecoveryDelay?: number
44-
memoryLimit?: number
4534
recordLimit?: number
35+
dropFactor?: number
36+
scavengeCycle?: number
4637
}
4738
}
4839

src/index.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
* Batcher index
33
*/
44

5+
'use strict';
6+
57
/* Requires ------------------------------------------------------------------*/
8+
69
const queue = require('./queue.js');
710
const store = require('./store.js');
8-
const breaker = require('./breaker.js');
911
const {contextKey, recordKey} = require('./utils.js');
1012
const EventEmitter = require('events').EventEmitter;
1113
const {hydrateConfig} = require('./options');
@@ -32,14 +34,11 @@ class HaStore extends EventEmitter {
3234
this.setMaxListeners(Infinity);
3335
}
3436

35-
this.breaker = breaker(this.config, this);
36-
3737
this.queue = queue(
3838
this.config,
3939
this,
4040
store(this.config, this),
4141
this.config.store,
42-
this.breaker,
4342
);
4443
}
4544

src/options.js

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
1+
/**
2+
* Options
3+
*/
4+
5+
'use strict';
6+
7+
/* Requires ------------------------------------------------------------------*/
8+
19
const {exp} = require('./utils.js');
210

11+
/* Local variables -----------------------------------------------------------*/
12+
313
const defaultConfig = {
414
batch: {
515
tick: 50,
@@ -17,34 +27,28 @@ const defaultConfig = {
1727
limit: 30000,
1828
curve: exp,
1929
},
20-
breaker: {
21-
base: 1000,
22-
steps: 10,
23-
limit: 0xffff,
24-
curve: exp,
25-
tolerance: 1,
26-
toleranceFrame: 10000,
27-
},
2830
};
2931

3032
const defaultStoreOptions = {
3133
pluginRecoveryDelay: 10000,
3234
pluginFallback: true,
33-
memoryLimit: 0.9,
34-
recordLimit: Infinity,
35+
recordLimit: 256 * 256,
36+
dropFactor: 1,
37+
scavengeCycle: 50,
3538
};
3639

3740
/* Methods -------------------------------------------------------------------*/
41+
3842
function hydrateStoreOptions(storeOptions = {}) {
3943
return {
4044
...defaultStoreOptions,
4145
...storeOptions,
4246
pluginRecoveryDelay: Number(storeOptions.pluginRecoveryDelay) || defaultStoreOptions.pluginRecoveryDelay,
4347
pluginFallback: (storeOptions.pluginFallback === undefined) ? true : storeOptions.pluginFallback,
44-
memoryLimit: Math.max(0, Math.min(1, Number(storeOptions.memoryLimit) || defaultStoreOptions.memoryLimit)),
4548
recordLimit: Number(storeOptions.recordLimit) || defaultStoreOptions.recordLimit,
49+
scavengeCycle: Number(storeOptions.scavengeCycle) || defaultStoreOptions.scavengeCycle,
50+
dropFactor: (storeOptions.dropFactor === undefined) ? defaultStoreOptions.dropFactor : Number(storeOptions.dropFactor),
4651
};
47-
4852
}
4953

5054
function hydrateIfNotNull(baseConfig, defaultConfig) {
@@ -72,8 +76,9 @@ function hydrateConfig(config = {}) {
7276
batch: hydrateIfNotNull(config.batch, defaultConfig.batch),
7377
retry: hydrateIfNotNull(config.retry, defaultConfig.retry),
7478
cache: hydrateIfNotNull(config.cache, defaultConfig.cache),
75-
breaker: hydrateIfNotNull(config.breaker, defaultConfig.breaker),
7679
};
7780
}
7881

82+
/* Exports -------------------------------------------------------------------*/
83+
7984
module.exports = {hydrateConfig, hydrateStoreOptions};

src/queue.js

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const contextRecordKey = key => id => recordKey(key, id);
1313

1414
/* Methods -------------------------------------------------------------------*/
1515

16-
function queue(config, emitter, store, storePlugin, breaker) {
16+
function queue(config, emitter, store, storePlugin) {
1717

1818
// Local variables
1919
const contexts = new Map();
@@ -107,12 +107,11 @@ function queue(config, emitter, store, storePlugin, breaker) {
107107
query(type, key, ids.splice(0, optimalBatchSize), context);
108108
}
109109

110-
contexts.delete(context.key)
110+
contexts.delete(context.key);
111111
}
112112

113113
/**
114114
* Main queue function
115-
* - Checks circuit-breaker status
116115
* - Resolves context object and deferred handlers
117116
* - Looks-up cache
118117
* - Prepares data-source query timer/invocation
@@ -123,7 +122,6 @@ function queue(config, emitter, store, storePlugin, breaker) {
123122
* @param {boolean} startQueue Wether to start the queue immediately or not
124123
*/
125124
async function push(id, params, agg, startQueue, uid) {
126-
if (breaker.status().active === true) return Promise.reject(breaker.circuitError);
127125
const key = contextKey(config.uniqueParams, params);
128126
const context = resolveContext(key, params, uid);
129127
let entity = await lookupCache(key, id, context);
@@ -193,7 +191,9 @@ function queue(config, emitter, store, storePlugin, breaker) {
193191
context.promises.delete(ids[i]);
194192
}
195193
}
196-
if (context.promises.size === 0) contexts.delete(context.key);
194+
if (context.promises.size === 0) {
195+
contexts.delete(context.key);
196+
}
197197
}
198198

199199
function handleQueryCriticalError(err, override) {
@@ -241,7 +241,6 @@ function queue(config, emitter, store, storePlugin, breaker) {
241241
}
242242

243243
if (context.promises.size === 0) contexts.delete(context.key);
244-
breaker.openCircuit();
245244
}
246245
}
247246

@@ -260,9 +259,6 @@ function queue(config, emitter, store, storePlugin, breaker) {
260259
if (config.cache) {
261260
targetStore.set(contextRecordKey(key), ids.filter(id => records[id] !== null && records[id] !== undefined), records, { step: 0 });
262261
}
263-
if (breaker.status().step > 0) {
264-
breaker.closeCircuit();
265-
}
266262

267263
for (let i = 0; i < ids.length; i++) {
268264
const expectation = context.promises.get(ids[i]);

0 commit comments

Comments
 (0)