@weave-js/core 0.11.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -1
- package/jest.config.js +1 -1
- package/lib/ExtendableError.js +8 -8
- package/lib/broker/context.js +49 -49
- package/lib/broker/defaultOptions.js +8 -9
- package/lib/broker/index.js +160 -164
- package/lib/buildRuntime.js +34 -34
- package/lib/cache/adapters/base.js +50 -74
- package/lib/cache/adapters/inMemory.js +58 -58
- package/lib/cache/adapters/inMemoryLru.js +57 -57
- package/lib/cache/adapters/index.js +3 -39
- package/lib/cache/getCacheKeyByObject.js +31 -0
- package/lib/cache/getPropertyFromDataOrMetadata.js +20 -0
- package/lib/cache/lock.js +30 -24
- package/lib/constants.js +5 -5
- package/lib/errorHandler.js +8 -8
- package/lib/errors.js +64 -54
- package/lib/helper/defineAction.js +2 -2
- package/lib/helper/defineBrokerOptions.js +2 -2
- package/lib/helper/defineService.js +2 -2
- package/lib/index.js +21 -21
- package/lib/logger/base.js +38 -29
- package/lib/logger/format/asHumanReadable.js +15 -15
- package/lib/logger/format/asJson.js +10 -10
- package/lib/logger/format/index.js +3 -3
- package/lib/logger/index.js +24 -24
- package/lib/logger/levels.js +21 -21
- package/lib/logger/tools.js +15 -15
- package/lib/logger/utils/colorize.js +23 -23
- package/lib/logger/utils/format.js +75 -65
- package/lib/metrics/common.js +47 -47
- package/lib/metrics/constants.js +44 -44
- package/lib/metrics/exporter/base.js +8 -8
- package/lib/metrics/exporter/event.js +20 -20
- package/lib/metrics/exporter/index.js +15 -15
- package/lib/metrics/index.js +2 -2
- package/lib/metrics/types/base.js +20 -20
- package/lib/metrics/types/counter.js +6 -6
- package/lib/metrics/types/gauge.js +24 -24
- package/lib/metrics/types/histogram.js +26 -26
- package/lib/metrics/types/index.js +6 -6
- package/lib/metrics/types/info.js +17 -17
- package/lib/middlewares/action-hooks/index.js +38 -38
- package/lib/middlewares/bulkhead/index.js +34 -34
- package/lib/middlewares/cache/index.js +37 -37
- package/lib/middlewares/circuit-breaker/index.js +62 -62
- package/lib/middlewares/context-tracker/index.js +39 -39
- package/lib/middlewares/error-handler/index.js +47 -15
- package/lib/middlewares/index.js +2 -2
- package/lib/middlewares/metrics/getMiddlewareWrapper.js +17 -17
- package/lib/middlewares/metrics/index.js +29 -29
- package/lib/middlewares/retry/index.js +15 -15
- package/lib/middlewares/timeout/index.js +16 -16
- package/lib/middlewares/tracing/index.js +37 -37
- package/lib/middlewares/tracing/tags.js +6 -6
- package/lib/middlewares/validator/index.js +31 -31
- package/lib/registry/actionEndpoint.js +8 -8
- package/lib/registry/collections/actionCollection.js +39 -35
- package/lib/registry/collections/endpointCollection.js +63 -63
- package/lib/registry/collections/eventCollection.js +62 -62
- package/lib/registry/collections/nodeCollection.js +45 -45
- package/lib/registry/collections/serviceCollection.js +61 -55
- package/lib/registry/eventEndpoint.js +14 -14
- package/lib/registry/load-balancer/base.js +3 -3
- package/lib/registry/load-balancer/index.js +7 -7
- package/lib/registry/node.js +27 -27
- package/lib/registry/registry.js +139 -136
- package/lib/registry/service/parseAction.js +16 -16
- package/lib/registry/service/parseEvent.js +18 -18
- package/lib/registry/service/reduceMixins.js +10 -10
- package/lib/registry/service/service.js +72 -72
- package/lib/registry/serviceItem.js +21 -21
- package/lib/runtime/initActionInvoker.js +21 -21
- package/lib/runtime/initCache.js +7 -7
- package/lib/runtime/initContextFactory.js +22 -22
- package/lib/runtime/initEventbus.js +49 -49
- package/lib/runtime/initLogger.js +11 -11
- package/lib/runtime/initMetrics.js +54 -54
- package/lib/runtime/initMiddlewareManager.js +21 -21
- package/lib/runtime/initRegistry.js +6 -6
- package/lib/runtime/initServiceManager.js +90 -68
- package/lib/runtime/initTracing.js +23 -23
- package/lib/runtime/initTransport.js +5 -5
- package/lib/runtime/initUuidFactory.js +4 -4
- package/lib/runtime/initValidator.js +5 -5
- package/lib/tracing/collectors/base.js +22 -22
- package/lib/tracing/collectors/event.js +33 -33
- package/lib/tracing/collectors/index.js +15 -15
- package/lib/tracing/span.js +26 -26
- package/lib/tracing/time.js +8 -8
- package/lib/transport/InboundTransformStream.js +26 -26
- package/lib/transport/adapters/adapteBase.js +30 -30
- package/lib/transport/adapters/adapters.js +4 -4
- package/lib/transport/adapters/dummy/index.js +18 -18
- package/lib/transport/adapters/fromURI.js +13 -13
- package/lib/transport/adapters/getAdapterByName.js +5 -5
- package/lib/transport/adapters/index.js +12 -12
- package/lib/transport/adapters/tcp/discovery/codec.js +4 -4
- package/lib/transport/adapters/tcp/discovery/index.js +62 -62
- package/lib/transport/adapters/tcp/index.js +169 -171
- package/lib/transport/adapters/tcp/tcp-messagetypes.js +7 -7
- package/lib/transport/adapters/tcp/tcpReader.js +39 -39
- package/lib/transport/adapters/tcp/tcpWriteStream.js +26 -26
- package/lib/transport/adapters/tcp/tcpWriter.js +57 -57
- package/lib/transport/adapters/tcp/utils.js +12 -12
- package/lib/transport/createMessage.js +4 -4
- package/lib/transport/createTransport.js +254 -254
- package/lib/transport/errorPayloadFactory.js +13 -17
- package/lib/transport/messageHandlers.js +199 -206
- package/lib/transport/messageTypes.js +19 -19
- package/lib/types.js +28 -15
- package/lib/utils/index.js +1 -1
- package/lib/utils/options.js +44 -44
- package/lib/utils/restoreError.js +58 -6
- package/package.json +4 -4
- package/lib/cache/base.js +0 -136
|
@@ -1,65 +1,65 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Author: Kevin Ries (kevin@
|
|
2
|
+
* Author: Kevin Ries (kevin.ries@fachwerk.io)
|
|
3
3
|
* -----
|
|
4
4
|
* Copyright 2021 Fachwerk
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
function callHook (hook, service, context, result) {
|
|
8
8
|
if (typeof hook === 'function') {
|
|
9
|
-
return hook.call(service, context, result)
|
|
9
|
+
return hook.call(service, context, result);
|
|
10
10
|
} else if (Array.isArray(hook)) {
|
|
11
11
|
return hook.reduce(
|
|
12
12
|
(promise, fn) => promise.then(res => fn.call(service, context, res)),
|
|
13
|
-
Promise.resolve(result))
|
|
13
|
+
Promise.resolve(result));
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function callErrorHook (hook, service, context, error) {
|
|
18
18
|
if (typeof hook === 'function') {
|
|
19
|
-
return hook.call(service, context, error)
|
|
19
|
+
return hook.call(service, context, error);
|
|
20
20
|
} else if (Array.isArray(hook)) {
|
|
21
21
|
return hook.reduce(
|
|
22
22
|
(promise, fn) => promise.catch(err => fn.call(service, context, err)),
|
|
23
|
-
Promise.reject(error))
|
|
23
|
+
Promise.reject(error));
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function sanitizeHooks (hooks, service) {
|
|
28
28
|
if (typeof hooks === 'string') {
|
|
29
|
-
hooks = hooks.split(' ')
|
|
29
|
+
hooks = hooks.split(' ');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
if (Array.isArray(hooks)) {
|
|
33
33
|
return hooks.map((hook) => {
|
|
34
34
|
// resolve the method name
|
|
35
35
|
if (typeof hook === 'string') {
|
|
36
|
-
return (service && typeof service[hook] === 'function') ? service[hook] : null
|
|
36
|
+
return (service && typeof service[hook] === 'function') ? service[hook] : null;
|
|
37
37
|
}
|
|
38
|
-
return hook
|
|
39
|
-
})
|
|
38
|
+
return hook;
|
|
39
|
+
});
|
|
40
40
|
}
|
|
41
|
-
return hooks
|
|
41
|
+
return hooks;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const makeActionHookMiddleware = function (handler, action) {
|
|
45
|
-
const name = action.shortName
|
|
46
|
-
const hooks = (action.service && action.service.schema) ? action.service.schema.hooks : null
|
|
45
|
+
const name = action.shortName;
|
|
46
|
+
const hooks = (action.service && action.service.schema) ? action.service.schema.hooks : null;
|
|
47
47
|
|
|
48
48
|
if (hooks || action.hooks) {
|
|
49
49
|
// Wildcard hooks
|
|
50
|
-
const beforeWildcardHook = hooks && hooks.before ? sanitizeHooks(hooks.before['*'], action.service) : null
|
|
51
|
-
const afterWildcardHook = hooks && hooks.after ? sanitizeHooks(hooks.after['*'], action.service) : null
|
|
52
|
-
const errorWildcardHook = hooks && hooks.error ? sanitizeHooks(hooks.error['*'], action.service) : null
|
|
50
|
+
const beforeWildcardHook = hooks && hooks.before ? sanitizeHooks(hooks.before['*'], action.service) : null;
|
|
51
|
+
const afterWildcardHook = hooks && hooks.after ? sanitizeHooks(hooks.after['*'], action.service) : null;
|
|
52
|
+
const errorWildcardHook = hooks && hooks.error ? sanitizeHooks(hooks.error['*'], action.service) : null;
|
|
53
53
|
|
|
54
54
|
// Action name-related hooks
|
|
55
|
-
const beforeHook = hooks && hooks.before ? sanitizeHooks(hooks.before[name], action.service) : null
|
|
56
|
-
const afterHook = hooks && hooks.after ? sanitizeHooks(hooks.after[name], action.service) : null
|
|
57
|
-
const errorHook = hooks && hooks.error ? sanitizeHooks(hooks.error[name], action.service) : null
|
|
55
|
+
const beforeHook = hooks && hooks.before ? sanitizeHooks(hooks.before[name], action.service) : null;
|
|
56
|
+
const afterHook = hooks && hooks.after ? sanitizeHooks(hooks.after[name], action.service) : null;
|
|
57
|
+
const errorHook = hooks && hooks.error ? sanitizeHooks(hooks.error[name], action.service) : null;
|
|
58
58
|
|
|
59
59
|
// Hooks in action definition
|
|
60
|
-
const actionBeforeHook = action.hooks && action.hooks.before ? sanitizeHooks(action.hooks.before, action.service) : null
|
|
61
|
-
const actionAfterHook = action.hooks && action.hooks.after ? sanitizeHooks(action.hooks.after, action.service) : null
|
|
62
|
-
const actionErrorHook = action.hooks && action.hooks.error ? sanitizeHooks(action.hooks.error, action.service) : null
|
|
60
|
+
const actionBeforeHook = action.hooks && action.hooks.before ? sanitizeHooks(action.hooks.before, action.service) : null;
|
|
61
|
+
const actionAfterHook = action.hooks && action.hooks.after ? sanitizeHooks(action.hooks.after, action.service) : null;
|
|
62
|
+
const actionErrorHook = action.hooks && action.hooks.error ? sanitizeHooks(action.hooks.error, action.service) : null;
|
|
63
63
|
|
|
64
64
|
if (
|
|
65
65
|
beforeWildcardHook ||
|
|
@@ -73,66 +73,66 @@ const makeActionHookMiddleware = function (handler, action) {
|
|
|
73
73
|
actionErrorHook
|
|
74
74
|
) {
|
|
75
75
|
return function actionHookMiddleware (context, serviceInjections) {
|
|
76
|
-
let promise = Promise.resolve()
|
|
76
|
+
let promise = Promise.resolve();
|
|
77
77
|
|
|
78
78
|
// before all hook
|
|
79
79
|
if (beforeWildcardHook) {
|
|
80
|
-
promise = promise.then(() => callHook(beforeWildcardHook, action.service, context))
|
|
80
|
+
promise = promise.then(() => callHook(beforeWildcardHook, action.service, context));
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// Before hook
|
|
84
84
|
if (beforeHook) {
|
|
85
|
-
promise = promise.then(() => callHook(beforeHook, action.service, context))
|
|
85
|
+
promise = promise.then(() => callHook(beforeHook, action.service, context));
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// Before hook
|
|
89
89
|
if (actionBeforeHook) {
|
|
90
|
-
promise = promise.then(() => callHook(actionBeforeHook, action.service, context))
|
|
90
|
+
promise = promise.then(() => callHook(actionBeforeHook, action.service, context));
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// Call action handler
|
|
94
|
-
promise = promise.then(() => handler(context, serviceInjections))
|
|
94
|
+
promise = promise.then(() => handler(context, serviceInjections));
|
|
95
95
|
|
|
96
96
|
// After hook in action definition
|
|
97
97
|
if (actionAfterHook) {
|
|
98
|
-
promise = promise.then(result => callHook(actionAfterHook, action.service, context, result))
|
|
98
|
+
promise = promise.then(result => callHook(actionAfterHook, action.service, context, result));
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// After hook
|
|
102
102
|
if (afterHook) {
|
|
103
|
-
promise = promise.then(result => callHook(afterHook, action.service, context, result))
|
|
103
|
+
promise = promise.then(result => callHook(afterHook, action.service, context, result));
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// After wildcard hook
|
|
107
107
|
if (afterWildcardHook) {
|
|
108
|
-
promise = promise.then(result => callHook(afterWildcardHook, action.service, context, result))
|
|
108
|
+
promise = promise.then(result => callHook(afterWildcardHook, action.service, context, result));
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// Error hooks
|
|
112
112
|
// Error hook in action definition
|
|
113
113
|
if (actionErrorHook) {
|
|
114
|
-
promise = promise.catch(error => callErrorHook(actionErrorHook, action.service, context, error))
|
|
114
|
+
promise = promise.catch(error => callErrorHook(actionErrorHook, action.service, context, error));
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
// Error hook
|
|
118
118
|
if (errorHook) {
|
|
119
|
-
promise = promise.catch(error => callErrorHook(errorHook, action.service, context, error))
|
|
119
|
+
promise = promise.catch(error => callErrorHook(errorHook, action.service, context, error));
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
// Error wildcard hook
|
|
123
123
|
if (errorWildcardHook) {
|
|
124
|
-
promise = promise.catch(error => callErrorHook(errorWildcardHook, action.service, context, error))
|
|
124
|
+
promise = promise.catch(error => callErrorHook(errorWildcardHook, action.service, context, error));
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
return promise
|
|
128
|
-
}
|
|
127
|
+
return promise;
|
|
128
|
+
};
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
-
return handler
|
|
132
|
-
}
|
|
131
|
+
return handler;
|
|
132
|
+
};
|
|
133
133
|
|
|
134
134
|
module.exports = () => {
|
|
135
135
|
return {
|
|
136
136
|
localAction: makeActionHookMiddleware
|
|
137
|
-
}
|
|
138
|
-
}
|
|
137
|
+
};
|
|
138
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Author: Kevin Ries (kevin@
|
|
2
|
+
* Author: Kevin Ries (kevin.ries@fachwerk.io)
|
|
3
3
|
* -----
|
|
4
4
|
* Copyright 2021 Fachwerk
|
|
5
5
|
*/
|
|
6
|
-
const { Constants } = require('../../metrics')
|
|
7
|
-
const { WeaveQueueSizeExceededError } = require('../../errors')
|
|
6
|
+
const { Constants } = require('../../metrics');
|
|
7
|
+
const { WeaveQueueSizeExceededError } = require('../../errors');
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @typedef {import('../../types').Runtime} Runtime
|
|
@@ -18,47 +18,47 @@ const { WeaveQueueSizeExceededError } = require('../../errors')
|
|
|
18
18
|
*/
|
|
19
19
|
module.exports = (runtime) => {
|
|
20
20
|
const wrapBulkheadMiddleware = function (handler, action) {
|
|
21
|
-
const bulkheadOptions = runtime.options.bulkhead
|
|
21
|
+
const bulkheadOptions = runtime.options.bulkhead;
|
|
22
22
|
|
|
23
23
|
if (bulkheadOptions.enabled) {
|
|
24
|
-
const queue = []
|
|
25
|
-
let currentlyInFlight = 0
|
|
24
|
+
const queue = [];
|
|
25
|
+
let currentlyInFlight = 0;
|
|
26
26
|
|
|
27
27
|
const callNext = () => {
|
|
28
|
-
if (queue.length === 0) return
|
|
29
|
-
if (currentlyInFlight >= bulkheadOptions.concurrentCalls) return
|
|
28
|
+
if (queue.length === 0) return;
|
|
29
|
+
if (currentlyInFlight >= bulkheadOptions.concurrentCalls) return;
|
|
30
30
|
|
|
31
|
-
const item = queue.shift()
|
|
31
|
+
const item = queue.shift();
|
|
32
32
|
|
|
33
|
-
currentlyInFlight
|
|
33
|
+
currentlyInFlight++;
|
|
34
34
|
handler(item.context)
|
|
35
35
|
.then(result => {
|
|
36
|
-
currentlyInFlight
|
|
37
|
-
item.resolve(result)
|
|
38
|
-
callNext()
|
|
36
|
+
currentlyInFlight--;
|
|
37
|
+
item.resolve(result);
|
|
38
|
+
callNext();
|
|
39
39
|
})
|
|
40
40
|
.catch(error => {
|
|
41
|
-
currentlyInFlight
|
|
42
|
-
callNext()
|
|
43
|
-
return item.reject(error)
|
|
44
|
-
})
|
|
45
|
-
}
|
|
41
|
+
currentlyInFlight--;
|
|
42
|
+
callNext();
|
|
43
|
+
return item.reject(error);
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
46
|
|
|
47
47
|
return function bulkheadMiddlware (context, serviceInjections) {
|
|
48
48
|
// Execute action immediately
|
|
49
49
|
if (currentlyInFlight < bulkheadOptions.concurrentCalls) {
|
|
50
|
-
currentlyInFlight
|
|
50
|
+
currentlyInFlight++;
|
|
51
51
|
return handler(context, serviceInjections)
|
|
52
52
|
.then(result => {
|
|
53
|
-
currentlyInFlight
|
|
54
|
-
callNext()
|
|
55
|
-
return result
|
|
53
|
+
currentlyInFlight--;
|
|
54
|
+
callNext();
|
|
55
|
+
return result;
|
|
56
56
|
})
|
|
57
57
|
.catch(error => {
|
|
58
|
-
currentlyInFlight
|
|
59
|
-
callNext()
|
|
60
|
-
return Promise.reject(error)
|
|
61
|
-
})
|
|
58
|
+
currentlyInFlight--;
|
|
59
|
+
callNext();
|
|
60
|
+
return Promise.reject(error);
|
|
61
|
+
});
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// Reject the action if the max queue size is reached.
|
|
@@ -67,23 +67,23 @@ module.exports = (runtime) => {
|
|
|
67
67
|
action: action.name,
|
|
68
68
|
limit: bulkheadOptions.maxQueueSize,
|
|
69
69
|
size: queue.length
|
|
70
|
-
}))
|
|
70
|
+
}));
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// Queue the request
|
|
74
|
-
return new Promise((resolve, reject) => queue.push({ resolve, reject, context }))
|
|
75
|
-
}
|
|
74
|
+
return new Promise((resolve, reject) => queue.push({ resolve, reject, context }));
|
|
75
|
+
};
|
|
76
76
|
}
|
|
77
|
-
return handler
|
|
78
|
-
}
|
|
77
|
+
return handler;
|
|
78
|
+
};
|
|
79
79
|
|
|
80
80
|
return {
|
|
81
81
|
created () {
|
|
82
82
|
if (runtime.options.metrics.enabled) {
|
|
83
83
|
// todo: add bulkhead metrics
|
|
84
|
-
runtime.metrics.register({ type: 'gauge', name: Constants.BULKHEAD_REQUESTS_IN_FLIGHT, description: 'Number of in flight requests.' })
|
|
84
|
+
runtime.metrics.register({ type: 'gauge', name: Constants.BULKHEAD_REQUESTS_IN_FLIGHT, description: 'Number of in flight requests.' });
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
87
|
localAction: wrapBulkheadMiddleware
|
|
88
|
-
}
|
|
89
|
-
}
|
|
88
|
+
};
|
|
89
|
+
};
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
const { isString, isFunction } = require('@weave-js/utils')
|
|
1
|
+
const { isString, isFunction } = require('@weave-js/utils');
|
|
2
2
|
|
|
3
3
|
module.exports = (runtime) => {
|
|
4
4
|
return {
|
|
5
5
|
localAction: (handler, action) => {
|
|
6
|
-
const cacheOptions = runtime.options.cache
|
|
6
|
+
const cacheOptions = runtime.options.cache;
|
|
7
7
|
const cacheActionOptions = {
|
|
8
8
|
enabled: !!action.cache
|
|
9
|
-
}
|
|
9
|
+
};
|
|
10
10
|
|
|
11
11
|
if (isString(action.cache)) {
|
|
12
|
-
cacheActionOptions.keys = action.cache.split(' ')
|
|
12
|
+
cacheActionOptions.keys = action.cache.split(' ');
|
|
13
13
|
} else if (action.cache && Array.isArray(action.cache.keys)) {
|
|
14
|
-
cacheActionOptions.keys = action.cache.keys
|
|
14
|
+
cacheActionOptions.keys = action.cache.keys;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
if (cacheActionOptions.enabled) {
|
|
18
|
-
const cache = runtime.cache
|
|
19
|
-
const isEnabledFunction = isFunction(action.cache.condition)
|
|
18
|
+
const cache = runtime.cache;
|
|
19
|
+
const isEnabledFunction = isFunction(action.cache.condition);
|
|
20
20
|
|
|
21
21
|
return function cacheMiddleware (context, serviceInjections) {
|
|
22
22
|
// handle enabled function
|
|
23
23
|
if (isEnabledFunction) {
|
|
24
24
|
if (!action.cache.enabled.call(null, context)) {
|
|
25
25
|
// Enabled function returns "false". Cache is disabled.
|
|
26
|
-
return handler(context, serviceInjections)
|
|
26
|
+
return handler(context, serviceInjections);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -33,74 +33,74 @@ module.exports = (runtime) => {
|
|
|
33
33
|
context.data,
|
|
34
34
|
context.meta,
|
|
35
35
|
cacheActionOptions.keys
|
|
36
|
-
)
|
|
36
|
+
);
|
|
37
37
|
|
|
38
|
-
context.isCachedResult = false
|
|
38
|
+
context.isCachedResult = false;
|
|
39
39
|
|
|
40
40
|
// Disable caching by meta property.
|
|
41
41
|
if (context.meta.$noCache === true) {
|
|
42
|
-
return handler(context, serviceInjections)
|
|
42
|
+
return handler(context, serviceInjections);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// The cache adapter is not connected yet. In this case, we call the handler regular
|
|
46
46
|
if (cache.isConnected === false) {
|
|
47
|
-
cache.log('Cache adapter is not connected yet. Call handler...')
|
|
48
|
-
return handler(context, serviceInjections)
|
|
47
|
+
cache.log('Cache adapter is not connected yet. Call handler...');
|
|
48
|
+
return handler(context, serviceInjections);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
if (cacheOptions.lock.enabled) {
|
|
52
|
-
let cachePromise
|
|
52
|
+
let cachePromise;
|
|
53
53
|
if (cacheOptions.lock.staleTime && cache.getWithTTl) {
|
|
54
54
|
|
|
55
55
|
} else {
|
|
56
|
-
cachePromise = runtime.cache.get(cacheHashKey)
|
|
56
|
+
cachePromise = runtime.cache.get(cacheHashKey);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
return cachePromise.then((cachedResult) => {
|
|
60
60
|
if (cachedResult !== null) {
|
|
61
61
|
// Found a cached value. Skip calling handler and return our value
|
|
62
|
-
context.isCachedResult = true
|
|
63
|
-
return cachedResult
|
|
62
|
+
context.isCachedResult = true;
|
|
63
|
+
return cachedResult;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
return cache.lock(cacheHashKey).then((release) => {
|
|
67
67
|
return cache.get(cacheHashKey).then(cachedResult => {
|
|
68
68
|
if (cachedResult !== null) {
|
|
69
69
|
// Found a cached value. Skip calling handler and return our value
|
|
70
|
-
context.isCachedResult = true
|
|
70
|
+
context.isCachedResult = true;
|
|
71
71
|
return release().then(() => {
|
|
72
|
-
return cachedResult
|
|
73
|
-
})
|
|
72
|
+
return cachedResult;
|
|
73
|
+
});
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
return handler(context).then((result) => {
|
|
77
77
|
// Cache the value
|
|
78
78
|
cache.set(cacheHashKey, result, action.cache.ttl).then(() => {
|
|
79
79
|
// release the lock
|
|
80
|
-
release()
|
|
81
|
-
})
|
|
82
|
-
return result
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
})
|
|
80
|
+
release();
|
|
81
|
+
});
|
|
82
|
+
return result;
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// Not using cache lock
|
|
90
90
|
return runtime.cache.get(cacheHashKey).then((cachedResult) => {
|
|
91
91
|
if (cachedResult !== null) {
|
|
92
|
-
context.isCachedResult = true
|
|
93
|
-
return cachedResult
|
|
92
|
+
context.isCachedResult = true;
|
|
93
|
+
return cachedResult;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
return handler(context, serviceInjections).then((result) => {
|
|
97
|
-
runtime.cache.set(cacheHashKey, result, action.cache.ttl)
|
|
98
|
-
return result
|
|
99
|
-
})
|
|
100
|
-
})
|
|
101
|
-
}
|
|
97
|
+
runtime.cache.set(cacheHashKey, result, action.cache.ttl);
|
|
98
|
+
return result;
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
102
|
}
|
|
103
|
-
return handler
|
|
103
|
+
return handler;
|
|
104
104
|
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
105
|
+
};
|
|
106
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Author: Kevin Ries (kevin@
|
|
2
|
+
* Author: Kevin Ries (kevin.ries@fachwerk.io)
|
|
3
3
|
* -----
|
|
4
4
|
* Copyright 2021 Fachwerk
|
|
5
5
|
*/
|
|
@@ -8,32 +8,32 @@ const {
|
|
|
8
8
|
CIRCUIT_HALF_OPENED,
|
|
9
9
|
CIRCUIT_HALF_OPEN_WAITING,
|
|
10
10
|
CIRCUIT_OPENED
|
|
11
|
-
} = require('../../constants').circuitBreakerStates
|
|
11
|
+
} = require('../../constants').circuitBreakerStates;
|
|
12
12
|
|
|
13
13
|
module.exports = (runtime) => {
|
|
14
|
-
const storage = new Map()
|
|
15
|
-
let log = null
|
|
16
|
-
let circuitBreakerTimer = null
|
|
14
|
+
const storage = new Map();
|
|
15
|
+
let log = null;
|
|
16
|
+
let circuitBreakerTimer = null;
|
|
17
17
|
|
|
18
18
|
function createWindowTimer (windowTime) {
|
|
19
|
-
circuitBreakerTimer = setInterval(() => clearEndpointStore(), windowTime)
|
|
20
|
-
circuitBreakerTimer.unref()
|
|
19
|
+
circuitBreakerTimer = setInterval(() => clearEndpointStore(), windowTime);
|
|
20
|
+
circuitBreakerTimer.unref();
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function clearEndpointStore () {
|
|
24
24
|
storage.forEach((item) => {
|
|
25
25
|
if (item.callCounter === 0) {
|
|
26
|
-
storage.delete(item.name)
|
|
27
|
-
return
|
|
26
|
+
storage.delete(item.name);
|
|
27
|
+
return;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
item.callCounter = 0
|
|
31
|
-
item.failureCouter = 0
|
|
32
|
-
})
|
|
30
|
+
item.callCounter = 0;
|
|
31
|
+
item.failureCouter = 0;
|
|
32
|
+
});
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function getEndpointState (endpoint, options) {
|
|
36
|
-
let item = storage.get(endpoint.name)
|
|
36
|
+
let item = storage.get(endpoint.name);
|
|
37
37
|
if (!item) {
|
|
38
38
|
item = {
|
|
39
39
|
endpoint,
|
|
@@ -42,127 +42,127 @@ module.exports = (runtime) => {
|
|
|
42
42
|
failureCouter: 0,
|
|
43
43
|
state: CIRCUIT_CLOSED,
|
|
44
44
|
circuitBreakerTimer: null
|
|
45
|
-
}
|
|
46
|
-
storage.set(endpoint.name, item)
|
|
45
|
+
};
|
|
46
|
+
storage.set(endpoint.name, item);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
return item
|
|
49
|
+
return item;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function onSuccess (item, options) {
|
|
53
|
-
item.callCounter
|
|
53
|
+
item.callCounter++;
|
|
54
54
|
|
|
55
55
|
if (item.state === CIRCUIT_HALF_OPENED) {
|
|
56
|
-
closeCircuitBreaker(item)
|
|
56
|
+
closeCircuitBreaker(item);
|
|
57
57
|
} else {
|
|
58
|
-
checkThreshold(item, options)
|
|
58
|
+
checkThreshold(item, options);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function onFailure (item, options) {
|
|
63
|
-
item.callCounter
|
|
64
|
-
item.failureCouter
|
|
63
|
+
item.callCounter++;
|
|
64
|
+
item.failureCouter++;
|
|
65
65
|
|
|
66
|
-
checkThreshold(item, options)
|
|
66
|
+
checkThreshold(item, options);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function checkThreshold (item, options) {
|
|
70
70
|
if (item.failureCouter >= options.maxFailures) {
|
|
71
|
-
openCircuitBreaker(item)
|
|
71
|
+
openCircuitBreaker(item);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function openCircuitBreaker (item) {
|
|
76
|
-
item.state = CIRCUIT_OPENED
|
|
77
|
-
item.endpoint.state = false
|
|
78
|
-
item.circuitBreakerTimer = setTimeout(() => halfOpenCircuitBreaker(item), item.options.halfOpenTimeout)
|
|
79
|
-
item.circuitBreakerTimer.unref()
|
|
80
|
-
log.debug(`Circuit breaker has been opened for endpoint '${item.endpoint.name}'`)
|
|
76
|
+
item.state = CIRCUIT_OPENED;
|
|
77
|
+
item.endpoint.state = false;
|
|
78
|
+
item.circuitBreakerTimer = setTimeout(() => halfOpenCircuitBreaker(item), item.options.halfOpenTimeout);
|
|
79
|
+
item.circuitBreakerTimer.unref();
|
|
80
|
+
log.debug(`Circuit breaker has been opened for endpoint '${item.endpoint.name}'`);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
function halfOpenCircuitBreaker (item) {
|
|
84
|
-
item.state = CIRCUIT_HALF_OPENED
|
|
85
|
-
item.endpoint.state = true
|
|
84
|
+
item.state = CIRCUIT_HALF_OPENED;
|
|
85
|
+
item.endpoint.state = true;
|
|
86
86
|
|
|
87
|
-
log.debug(`Circuit breaker has been half opened for endpoint '${item.endpoint.name}'`)
|
|
87
|
+
log.debug(`Circuit breaker has been half opened for endpoint '${item.endpoint.name}'`);
|
|
88
88
|
|
|
89
89
|
if (item.circuitBreakerTimer) {
|
|
90
|
-
clearTimeout(item.circuitBreakerTimer)
|
|
91
|
-
item.circuitBreakerTimer = null
|
|
90
|
+
clearTimeout(item.circuitBreakerTimer);
|
|
91
|
+
item.circuitBreakerTimer = null;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
function handleHalfOpen (item) {
|
|
96
|
-
item.state = CIRCUIT_HALF_OPEN_WAITING
|
|
97
|
-
item.endpoint.state = false
|
|
98
|
-
item.circuitBreakerTimer = setTimeout(() => halfOpenCircuitBreaker(item), item.options.halfOpenTimeout)
|
|
99
|
-
item.circuitBreakerTimer.unref()
|
|
96
|
+
item.state = CIRCUIT_HALF_OPEN_WAITING;
|
|
97
|
+
item.endpoint.state = false;
|
|
98
|
+
item.circuitBreakerTimer = setTimeout(() => halfOpenCircuitBreaker(item), item.options.halfOpenTimeout);
|
|
99
|
+
item.circuitBreakerTimer.unref();
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
function closeCircuitBreaker (item) {
|
|
103
|
-
item.failureCouter = 0
|
|
104
|
-
item.callCounter = 0
|
|
105
|
-
item.state = CIRCUIT_CLOSED
|
|
106
|
-
item.endpoint.state = true
|
|
103
|
+
item.failureCouter = 0;
|
|
104
|
+
item.callCounter = 0;
|
|
105
|
+
item.state = CIRCUIT_CLOSED;
|
|
106
|
+
item.endpoint.state = true;
|
|
107
107
|
|
|
108
108
|
if (item.circuitBreakerTimer) {
|
|
109
|
-
clearTimeout(item.circuitBreakerTimer)
|
|
110
|
-
item.circuitBreakerTimer = null
|
|
109
|
+
clearTimeout(item.circuitBreakerTimer);
|
|
110
|
+
item.circuitBreakerTimer = null;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
log.debug(`Circuit breaker has been closed for endpoint '${item.endpoint.name}'`)
|
|
113
|
+
log.debug(`Circuit breaker has been closed for endpoint '${item.endpoint.name}'`);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
function wrapCircuitBreakerMiddleware (handler, action) {
|
|
117
|
-
const options = Object.assign({}, runtime.options.circuitBreaker, action.circuitBreaker || {})
|
|
117
|
+
const options = Object.assign({}, runtime.options.circuitBreaker, action.circuitBreaker || {});
|
|
118
118
|
|
|
119
119
|
if (options.enabled) {
|
|
120
120
|
return function curcuitBreakerMiddleware (context, serviceInjections) {
|
|
121
|
-
const endpoint = context.endpoint
|
|
122
|
-
const item = getEndpointState(endpoint, options)
|
|
121
|
+
const endpoint = context.endpoint;
|
|
122
|
+
const item = getEndpointState(endpoint, options);
|
|
123
123
|
|
|
124
124
|
// handle half open states
|
|
125
125
|
if (item.state === CIRCUIT_HALF_OPENED) {
|
|
126
|
-
handleHalfOpen(item, context)
|
|
126
|
+
handleHalfOpen(item, context);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
return handler(context, serviceInjections)
|
|
130
130
|
.then(result => {
|
|
131
|
-
const item = getEndpointState(endpoint, options)
|
|
132
|
-
onSuccess(item, options)
|
|
131
|
+
const item = getEndpointState(endpoint, options);
|
|
132
|
+
onSuccess(item, options);
|
|
133
133
|
|
|
134
|
-
return result
|
|
134
|
+
return result;
|
|
135
135
|
})
|
|
136
136
|
.catch(error => {
|
|
137
137
|
if (item && (!error.nodeId || error.nodeId === context.nodeId)) {
|
|
138
|
-
onFailure(item, options)
|
|
138
|
+
onFailure(item, options);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
return Promise.reject(error)
|
|
142
|
-
})
|
|
143
|
-
}
|
|
141
|
+
return Promise.reject(error);
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
144
|
}
|
|
145
|
-
return handler
|
|
145
|
+
return handler;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
return {
|
|
149
149
|
created () {
|
|
150
|
-
log = runtime.createLogger('circuit-breaker')
|
|
150
|
+
log = runtime.createLogger('circuit-breaker');
|
|
151
151
|
|
|
152
152
|
if (runtime.options.metrics.enabled) {
|
|
153
153
|
// todo: add circuit breaker metrics
|
|
154
154
|
}
|
|
155
155
|
},
|
|
156
156
|
started () {
|
|
157
|
-
const { enabled, windowTime } = this.options.circuitBreaker
|
|
157
|
+
const { enabled, windowTime } = this.options.circuitBreaker;
|
|
158
158
|
if (enabled) {
|
|
159
|
-
createWindowTimer(windowTime)
|
|
159
|
+
createWindowTimer(windowTime);
|
|
160
160
|
}
|
|
161
161
|
},
|
|
162
162
|
localAction: wrapCircuitBreakerMiddleware,
|
|
163
163
|
remoteAction: wrapCircuitBreakerMiddleware,
|
|
164
164
|
brokerStopped () {
|
|
165
|
-
clearInterval(circuitBreakerTimer)
|
|
165
|
+
clearInterval(circuitBreakerTimer);
|
|
166
166
|
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
167
|
+
};
|
|
168
|
+
};
|