@weave-js/core 0.10.0 → 0.12.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 +33 -0
- package/lib/broker/context.js +58 -46
- package/lib/broker/{default-options.js → defaultOptions.js} +31 -13
- package/lib/broker/index.js +273 -251
- package/lib/buildRuntime.js +70 -0
- package/lib/cache/adapters/base.js +119 -0
- package/lib/cache/adapters/inMemory.js +154 -0
- package/lib/cache/adapters/inMemoryLru.js +140 -0
- package/lib/cache/adapters/index.js +10 -0
- package/lib/cache/getCacheKeyByObject.js +31 -0
- package/lib/cache/getPropertyFromDataOrMetadata.js +20 -0
- package/lib/cache/lock.js +34 -11
- package/lib/constants.js +11 -8
- package/lib/{error-handler.js → errorHandler.js} +8 -8
- package/lib/errors.js +46 -39
- package/lib/helper/{define-action.js → defineAction.js} +5 -3
- package/lib/helper/{define-broker-options.js → defineBrokerOptions.js} +4 -2
- package/lib/helper/{define-service.js → defineService.js} +6 -3
- package/lib/index.js +25 -22
- package/lib/logger/base.js +38 -29
- package/lib/logger/format/asHumanReadable.js +15 -14
- package/lib/logger/format/asJson.js +10 -10
- package/lib/logger/format/index.js +3 -3
- package/lib/logger/index.js +28 -25
- package/lib/logger/levels.js +23 -22
- package/lib/logger/tools.js +17 -13
- package/lib/logger/utils/colorize.js +23 -23
- package/lib/logger/utils/format.js +89 -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 +82 -34
- package/lib/middlewares/bulkhead/index.js +34 -33
- package/lib/middlewares/cache/index.js +106 -0
- package/lib/middlewares/circuit-breaker/index.js +85 -60
- package/lib/middlewares/context-tracker/index.js +39 -39
- package/lib/middlewares/error-handler/index.js +14 -14
- package/lib/middlewares/index.js +3 -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 +18 -18
- package/lib/middlewares/tracing/index.js +50 -37
- package/lib/middlewares/tracing/tags.js +43 -0
- package/lib/middlewares/validator/index.js +32 -32
- package/lib/registry/{action-endpoint.js → actionEndpoint.js} +8 -8
- package/lib/registry/collections/{action-collection.js → actionCollection.js} +39 -35
- package/lib/registry/collections/{endpoint-collection.js → endpointCollection.js} +64 -65
- package/lib/registry/collections/{event-collection.js → eventCollection.js} +65 -69
- package/lib/registry/collections/{node-collection.js → nodeCollection.js} +45 -45
- package/lib/registry/collections/{service-collection.js → serviceCollection.js} +61 -55
- package/lib/registry/eventEndpoint.js +26 -0
- 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 +147 -163
- 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/{service-item.js → serviceItem.js} +21 -21
- package/lib/runtime/{init-action-invoker.js → initActionInvoker.js} +21 -21
- package/lib/runtime/initCache.js +19 -0
- package/lib/runtime/{init-context-factory.js → initContextFactory.js} +22 -22
- package/lib/runtime/{init-eventbus.js → initEventbus.js} +54 -53
- package/lib/runtime/{init-logger.js → initLogger.js} +11 -11
- package/lib/runtime/{init-metrics.js → initMetrics.js} +55 -54
- package/lib/runtime/{init-middleware-manager.js → initMiddlewareManager.js} +21 -21
- package/lib/runtime/{init-registry.js → initRegistry.js} +6 -6
- package/lib/runtime/initServiceManager.js +145 -0
- package/lib/runtime/{init-tracing.js → initTracing.js} +25 -24
- package/lib/runtime/{init-transport.js → initTransport.js} +5 -5
- package/lib/runtime/{init-uuid-factory.js → initUuidFactory.js} +4 -4
- package/lib/runtime/{init-validator.js → initValidator.js} +5 -5
- package/lib/tracing/collectors/base.js +29 -37
- package/lib/tracing/collectors/event.js +33 -33
- package/lib/tracing/collectors/index.js +16 -16
- package/lib/tracing/span.js +34 -32
- package/lib/tracing/time.js +8 -8
- package/lib/transport/InboundTransformStream.js +75 -0
- package/lib/transport/adapters/adapteBase.js +107 -0
- package/lib/transport/adapters/adapters.js +4 -4
- package/lib/transport/adapters/dummy/index.js +19 -19
- 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 +90 -76
- package/lib/transport/adapters/tcp/index.js +180 -175
- package/lib/transport/adapters/tcp/tcp-messagetypes.js +7 -7
- package/lib/transport/adapters/tcp/tcpReader.js +39 -38
- package/lib/transport/adapters/tcp/tcpWriteStream.js +26 -26
- package/lib/transport/adapters/tcp/tcpWriter.js +58 -58
- package/lib/transport/adapters/tcp/utils.js +12 -12
- package/lib/transport/createMessage.js +11 -0
- package/lib/transport/createTransport.js +377 -334
- package/lib/transport/errorPayloadFactory.js +15 -0
- package/lib/transport/messageHandlers.js +517 -0
- package/lib/transport/messageTypes.js +23 -0
- package/lib/types.js +95 -71
- package/lib/utils/index.js +2 -2
- package/lib/utils/options.js +47 -45
- package/lib/utils/{restore-error.js → restoreError.js} +4 -4
- package/package.json +6 -12
- package/CHANGELOG.md +0 -1334
- package/lib/build-runtime.js +0 -70
- package/lib/cache/base.js +0 -142
- package/lib/cache/index.js +0 -48
- package/lib/cache/memory.js +0 -121
- package/lib/middlewares/tracing/buildTags.js +0 -17
- package/lib/new-errors.js +0 -25
- package/lib/registry/event-endpoint.js +0 -26
- package/lib/runtime/init-cache.js +0 -10
- package/lib/runtime/init-service-manager.js +0 -128
- package/lib/tracing/collectors/console.js +0 -34
- package/lib/tracing/index.js +0 -3
- package/lib/tracing/tracer.js +0 -64
- package/lib/transport/adapters/adapter-base.js +0 -92
- package/lib/transport/message-handlers.js +0 -448
- package/lib/transport/message-types.js +0 -59
- package/lib/transport/message.js +0 -16
|
@@ -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
|
-
|
|
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,22 +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
85
|
}
|
|
85
86
|
},
|
|
86
87
|
localAction: wrapBulkheadMiddleware
|
|
87
|
-
}
|
|
88
|
-
}
|
|
88
|
+
};
|
|
89
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const { isString, isFunction } = require('@weave-js/utils');
|
|
2
|
+
|
|
3
|
+
module.exports = (runtime) => {
|
|
4
|
+
return {
|
|
5
|
+
localAction: (handler, action) => {
|
|
6
|
+
const cacheOptions = runtime.options.cache;
|
|
7
|
+
const cacheActionOptions = {
|
|
8
|
+
enabled: !!action.cache
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
if (isString(action.cache)) {
|
|
12
|
+
cacheActionOptions.keys = action.cache.split(' ');
|
|
13
|
+
} else if (action.cache && Array.isArray(action.cache.keys)) {
|
|
14
|
+
cacheActionOptions.keys = action.cache.keys;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (cacheActionOptions.enabled) {
|
|
18
|
+
const cache = runtime.cache;
|
|
19
|
+
const isEnabledFunction = isFunction(action.cache.condition);
|
|
20
|
+
|
|
21
|
+
return function cacheMiddleware (context, serviceInjections) {
|
|
22
|
+
// handle enabled function
|
|
23
|
+
if (isEnabledFunction) {
|
|
24
|
+
if (!action.cache.enabled.call(null, context)) {
|
|
25
|
+
// Enabled function returns "false". Cache is disabled.
|
|
26
|
+
return handler(context, serviceInjections);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Generate cache hash
|
|
31
|
+
const cacheHashKey = cache.getCachingKey(
|
|
32
|
+
action.name,
|
|
33
|
+
context.data,
|
|
34
|
+
context.meta,
|
|
35
|
+
cacheActionOptions.keys
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
context.isCachedResult = false;
|
|
39
|
+
|
|
40
|
+
// Disable caching by meta property.
|
|
41
|
+
if (context.meta.$noCache === true) {
|
|
42
|
+
return handler(context, serviceInjections);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// The cache adapter is not connected yet. In this case, we call the handler regular
|
|
46
|
+
if (cache.isConnected === false) {
|
|
47
|
+
cache.log('Cache adapter is not connected yet. Call handler...');
|
|
48
|
+
return handler(context, serviceInjections);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (cacheOptions.lock.enabled) {
|
|
52
|
+
let cachePromise;
|
|
53
|
+
if (cacheOptions.lock.staleTime && cache.getWithTTl) {
|
|
54
|
+
|
|
55
|
+
} else {
|
|
56
|
+
cachePromise = runtime.cache.get(cacheHashKey);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return cachePromise.then((cachedResult) => {
|
|
60
|
+
if (cachedResult !== null) {
|
|
61
|
+
// Found a cached value. Skip calling handler and return our value
|
|
62
|
+
context.isCachedResult = true;
|
|
63
|
+
return cachedResult;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return cache.lock(cacheHashKey).then((release) => {
|
|
67
|
+
return cache.get(cacheHashKey).then(cachedResult => {
|
|
68
|
+
if (cachedResult !== null) {
|
|
69
|
+
// Found a cached value. Skip calling handler and return our value
|
|
70
|
+
context.isCachedResult = true;
|
|
71
|
+
return release().then(() => {
|
|
72
|
+
return cachedResult;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return handler(context).then((result) => {
|
|
77
|
+
// Cache the value
|
|
78
|
+
cache.set(cacheHashKey, result, action.cache.ttl).then(() => {
|
|
79
|
+
// release the lock
|
|
80
|
+
release();
|
|
81
|
+
});
|
|
82
|
+
return result;
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Not using cache lock
|
|
90
|
+
return runtime.cache.get(cacheHashKey).then((cachedResult) => {
|
|
91
|
+
if (cachedResult !== null) {
|
|
92
|
+
context.isCachedResult = true;
|
|
93
|
+
return cachedResult;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return handler(context, serviceInjections).then((result) => {
|
|
97
|
+
runtime.cache.set(cacheHashKey, result, action.cache.ttl);
|
|
98
|
+
return result;
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return handler;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
};
|
|
@@ -1,143 +1,168 @@
|
|
|
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 {
|
|
7
|
+
CIRCUIT_CLOSED,
|
|
8
|
+
CIRCUIT_HALF_OPENED,
|
|
9
|
+
CIRCUIT_HALF_OPEN_WAITING,
|
|
10
|
+
CIRCUIT_OPENED
|
|
11
|
+
} = require('../../constants').circuitBreakerStates;
|
|
6
12
|
|
|
7
13
|
module.exports = (runtime) => {
|
|
8
|
-
const storage = new Map()
|
|
9
|
-
let log = null
|
|
10
|
-
let circuitBreakerTimer = null
|
|
14
|
+
const storage = new Map();
|
|
15
|
+
let log = null;
|
|
16
|
+
let circuitBreakerTimer = null;
|
|
11
17
|
|
|
12
18
|
function createWindowTimer (windowTime) {
|
|
13
|
-
circuitBreakerTimer = setInterval(() => clearEndpointStore(), windowTime)
|
|
14
|
-
circuitBreakerTimer.unref()
|
|
19
|
+
circuitBreakerTimer = setInterval(() => clearEndpointStore(), windowTime);
|
|
20
|
+
circuitBreakerTimer.unref();
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
function clearEndpointStore () {
|
|
18
|
-
storage.forEach(item => {
|
|
24
|
+
storage.forEach((item) => {
|
|
19
25
|
if (item.callCounter === 0) {
|
|
20
|
-
storage.delete(item.name)
|
|
21
|
-
return
|
|
26
|
+
storage.delete(item.name);
|
|
27
|
+
return;
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
item.callCounter = 0
|
|
25
|
-
item.failureCouter = 0
|
|
26
|
-
})
|
|
30
|
+
item.callCounter = 0;
|
|
31
|
+
item.failureCouter = 0;
|
|
32
|
+
});
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
function getEndpointState (endpoint, options) {
|
|
30
|
-
let item = storage.get(endpoint.name)
|
|
36
|
+
let item = storage.get(endpoint.name);
|
|
31
37
|
if (!item) {
|
|
32
38
|
item = {
|
|
33
39
|
endpoint,
|
|
34
40
|
options,
|
|
35
41
|
callCounter: 0,
|
|
36
42
|
failureCouter: 0,
|
|
37
|
-
state:
|
|
43
|
+
state: CIRCUIT_CLOSED,
|
|
38
44
|
circuitBreakerTimer: null
|
|
39
|
-
}
|
|
40
|
-
storage.set(endpoint.name, item)
|
|
45
|
+
};
|
|
46
|
+
storage.set(endpoint.name, item);
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
return item
|
|
49
|
+
return item;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
function
|
|
47
|
-
item.callCounter
|
|
52
|
+
function onSuccess (item, options) {
|
|
53
|
+
item.callCounter++;
|
|
48
54
|
|
|
49
|
-
if (item.state ===
|
|
50
|
-
closeCircuitBreaker(item
|
|
55
|
+
if (item.state === CIRCUIT_HALF_OPENED) {
|
|
56
|
+
closeCircuitBreaker(item);
|
|
51
57
|
} else {
|
|
52
|
-
checkThreshold(item, options)
|
|
58
|
+
checkThreshold(item, options);
|
|
53
59
|
}
|
|
54
60
|
}
|
|
55
61
|
|
|
56
|
-
function
|
|
57
|
-
item.callCounter
|
|
58
|
-
item.failureCouter
|
|
62
|
+
function onFailure (item, options) {
|
|
63
|
+
item.callCounter++;
|
|
64
|
+
item.failureCouter++;
|
|
59
65
|
|
|
60
|
-
checkThreshold(item, options)
|
|
66
|
+
checkThreshold(item, options);
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
function checkThreshold (item, options) {
|
|
64
70
|
if (item.failureCouter >= options.maxFailures) {
|
|
65
|
-
openCircuitBreaker(item)
|
|
71
|
+
openCircuitBreaker(item);
|
|
66
72
|
}
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
function openCircuitBreaker (item) {
|
|
70
|
-
item.state =
|
|
71
|
-
item.endpoint.state = false
|
|
72
|
-
item.circuitBreakerTimer = setTimeout(() => halfOpenCircuitBreaker(item), item.options.halfOpenTimeout)
|
|
73
|
-
|
|
74
|
-
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}'`);
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
function halfOpenCircuitBreaker (item) {
|
|
78
|
-
item.state =
|
|
79
|
-
item.endpoint.state = true
|
|
84
|
+
item.state = CIRCUIT_HALF_OPENED;
|
|
85
|
+
item.endpoint.state = true;
|
|
86
|
+
|
|
87
|
+
log.debug(`Circuit breaker has been half opened for endpoint '${item.endpoint.name}'`);
|
|
80
88
|
|
|
81
|
-
|
|
89
|
+
if (item.circuitBreakerTimer) {
|
|
90
|
+
clearTimeout(item.circuitBreakerTimer);
|
|
91
|
+
item.circuitBreakerTimer = null;
|
|
92
|
+
}
|
|
82
93
|
}
|
|
83
94
|
|
|
84
|
-
function
|
|
85
|
-
item.
|
|
86
|
-
item.
|
|
87
|
-
item.
|
|
88
|
-
item.
|
|
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();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function closeCircuitBreaker (item) {
|
|
103
|
+
item.failureCouter = 0;
|
|
104
|
+
item.callCounter = 0;
|
|
105
|
+
item.state = CIRCUIT_CLOSED;
|
|
106
|
+
item.endpoint.state = true;
|
|
89
107
|
|
|
90
108
|
if (item.circuitBreakerTimer) {
|
|
91
|
-
clearTimeout(item.circuitBreakerTimer)
|
|
92
|
-
item.circuitBreakerTimer = null
|
|
109
|
+
clearTimeout(item.circuitBreakerTimer);
|
|
110
|
+
item.circuitBreakerTimer = null;
|
|
93
111
|
}
|
|
94
112
|
|
|
95
|
-
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}'`);
|
|
96
114
|
}
|
|
97
115
|
|
|
98
116
|
function wrapCircuitBreakerMiddleware (handler, action) {
|
|
99
|
-
const options = Object.assign({}, runtime.options.circuitBreaker, action.circuitBreaker || {})
|
|
117
|
+
const options = Object.assign({}, runtime.options.circuitBreaker, action.circuitBreaker || {});
|
|
100
118
|
|
|
101
119
|
if (options.enabled) {
|
|
102
120
|
return function curcuitBreakerMiddleware (context, serviceInjections) {
|
|
103
|
-
const endpoint = context.endpoint
|
|
104
|
-
const item = getEndpointState(endpoint, options)
|
|
121
|
+
const endpoint = context.endpoint;
|
|
122
|
+
const item = getEndpointState(endpoint, options);
|
|
123
|
+
|
|
124
|
+
// handle half open states
|
|
125
|
+
if (item.state === CIRCUIT_HALF_OPENED) {
|
|
126
|
+
handleHalfOpen(item, context);
|
|
127
|
+
}
|
|
105
128
|
|
|
106
129
|
return handler(context, serviceInjections)
|
|
107
130
|
.then(result => {
|
|
108
|
-
const item = getEndpointState(endpoint, options)
|
|
109
|
-
|
|
131
|
+
const item = getEndpointState(endpoint, options);
|
|
132
|
+
onSuccess(item, options);
|
|
110
133
|
|
|
111
|
-
return result
|
|
134
|
+
return result;
|
|
112
135
|
})
|
|
113
136
|
.catch(error => {
|
|
114
|
-
|
|
137
|
+
if (item && (!error.nodeId || error.nodeId === context.nodeId)) {
|
|
138
|
+
onFailure(item, options);
|
|
139
|
+
}
|
|
115
140
|
|
|
116
|
-
return Promise.reject(error)
|
|
117
|
-
})
|
|
118
|
-
}
|
|
141
|
+
return Promise.reject(error);
|
|
142
|
+
});
|
|
143
|
+
};
|
|
119
144
|
}
|
|
120
|
-
return handler
|
|
145
|
+
return handler;
|
|
121
146
|
}
|
|
122
147
|
|
|
123
148
|
return {
|
|
124
149
|
created () {
|
|
125
|
-
log = runtime.createLogger('circuit-breaker')
|
|
150
|
+
log = runtime.createLogger('circuit-breaker');
|
|
126
151
|
|
|
127
152
|
if (runtime.options.metrics.enabled) {
|
|
128
153
|
// todo: add circuit breaker metrics
|
|
129
154
|
}
|
|
130
155
|
},
|
|
131
156
|
started () {
|
|
132
|
-
const { enabled, windowTime } = this.options.circuitBreaker
|
|
157
|
+
const { enabled, windowTime } = this.options.circuitBreaker;
|
|
133
158
|
if (enabled) {
|
|
134
|
-
createWindowTimer(windowTime)
|
|
159
|
+
createWindowTimer(windowTime);
|
|
135
160
|
}
|
|
136
161
|
},
|
|
137
162
|
localAction: wrapCircuitBreakerMiddleware,
|
|
138
163
|
remoteAction: wrapCircuitBreakerMiddleware,
|
|
139
164
|
brokerStopped () {
|
|
140
|
-
clearInterval(circuitBreakerTimer)
|
|
165
|
+
clearInterval(circuitBreakerTimer);
|
|
141
166
|
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
167
|
+
};
|
|
168
|
+
};
|
|
@@ -1,107 +1,107 @@
|
|
|
1
|
-
const { WeaveGracefulStopTimeoutError } = require('../../errors')
|
|
1
|
+
const { WeaveGracefulStopTimeoutError } = require('../../errors');
|
|
2
2
|
|
|
3
3
|
module.exports = (runtime) => {
|
|
4
4
|
function addContext (context) {
|
|
5
5
|
if (context.service) {
|
|
6
6
|
// local actions
|
|
7
|
-
context.service._trackedContexts.push(context)
|
|
7
|
+
context.service._trackedContexts.push(context);
|
|
8
8
|
} else {
|
|
9
9
|
// remote actions
|
|
10
|
-
context.service._trackedContexts.push(context)
|
|
10
|
+
context.service._trackedContexts.push(context);
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
function removeContext (context) {
|
|
15
15
|
if (context.service) {
|
|
16
16
|
// local actions
|
|
17
|
-
const index = context.service._trackedContexts.indexOf(context)
|
|
17
|
+
const index = context.service._trackedContexts.indexOf(context);
|
|
18
18
|
if (index !== -1) {
|
|
19
|
-
context.service._trackedContexts.splice(index, 1)
|
|
19
|
+
context.service._trackedContexts.splice(index, 1);
|
|
20
20
|
}
|
|
21
21
|
} else {
|
|
22
|
-
const index = context.service._trackedContexts.indexOf(context)
|
|
22
|
+
const index = context.service._trackedContexts.indexOf(context);
|
|
23
23
|
if (index !== -1) {
|
|
24
|
-
context.service._trackedContexts.splice(index, 1)
|
|
24
|
+
context.service._trackedContexts.splice(index, 1);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function wrapContextTrackerMiddleware (actionHandler) {
|
|
30
30
|
return function ContextTrackerMiddleware (context, serviceInjections) {
|
|
31
|
-
const isTracked = context.options.track === true ? context.options.track : runtime.options.contextTracking.enabled
|
|
31
|
+
const isTracked = context.options.track === true ? context.options.track : runtime.options.contextTracking.enabled;
|
|
32
32
|
|
|
33
33
|
if (!isTracked) {
|
|
34
|
-
return actionHandler(context, serviceInjections)
|
|
34
|
+
return actionHandler(context, serviceInjections);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
addContext(context)
|
|
37
|
+
addContext(context);
|
|
38
38
|
|
|
39
39
|
return actionHandler(context, serviceInjections)
|
|
40
40
|
.then(result => {
|
|
41
|
-
removeContext(context)
|
|
42
|
-
return result
|
|
41
|
+
removeContext(context);
|
|
42
|
+
return result;
|
|
43
43
|
})
|
|
44
44
|
.catch(error => {
|
|
45
|
-
removeContext(context)
|
|
46
|
-
throw error
|
|
47
|
-
})
|
|
48
|
-
}
|
|
45
|
+
removeContext(context);
|
|
46
|
+
throw error;
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const waitingForActiveContexts = (contextList, log, shutdownTimeout, service) => {
|
|
52
52
|
return new Promise((resolve) => {
|
|
53
53
|
if (contextList.length === 0) {
|
|
54
|
-
resolve()
|
|
54
|
+
resolve();
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
let isTimedOut = false
|
|
57
|
+
let isTimedOut = false;
|
|
58
58
|
const timeout = setTimeout(() => {
|
|
59
|
-
isTimedOut = true
|
|
60
|
-
log.error(new WeaveGracefulStopTimeoutError(service))
|
|
61
|
-
contextList = []
|
|
62
|
-
resolve()
|
|
63
|
-
}, shutdownTimeout)
|
|
59
|
+
isTimedOut = true;
|
|
60
|
+
log.error(new WeaveGracefulStopTimeoutError(service));
|
|
61
|
+
contextList = [];
|
|
62
|
+
resolve();
|
|
63
|
+
}, shutdownTimeout);
|
|
64
64
|
|
|
65
|
-
let isFirstCheck = true
|
|
65
|
+
let isFirstCheck = true;
|
|
66
66
|
const checkContexts = () => {
|
|
67
67
|
if (contextList.length === 0) {
|
|
68
|
-
clearTimeout(timeout)
|
|
69
|
-
resolve()
|
|
68
|
+
clearTimeout(timeout);
|
|
69
|
+
resolve();
|
|
70
70
|
} else {
|
|
71
71
|
if (isFirstCheck) {
|
|
72
|
-
log.info(`Waiting for ${contextList.length} open Contexts...`)
|
|
73
|
-
isFirstCheck = false
|
|
72
|
+
log.info(`Waiting for ${contextList.length} open Contexts...`);
|
|
73
|
+
isFirstCheck = false;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
if (!isTimedOut) {
|
|
77
|
-
setTimeout(checkContexts, 100)
|
|
77
|
+
setTimeout(checkContexts, 100);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
}
|
|
81
|
-
setImmediate(checkContexts)
|
|
82
|
-
})
|
|
83
|
-
}
|
|
80
|
+
};
|
|
81
|
+
setImmediate(checkContexts);
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
84
|
|
|
85
85
|
return {
|
|
86
86
|
created () {
|
|
87
87
|
// init context-store
|
|
88
|
-
runtime.state.trackedContexts = []
|
|
88
|
+
runtime.state.trackedContexts = [];
|
|
89
89
|
},
|
|
90
90
|
serviceStarting (service) {
|
|
91
|
-
service._trackedContexts = []
|
|
91
|
+
service._trackedContexts = [];
|
|
92
92
|
},
|
|
93
93
|
|
|
94
94
|
// Before a local service stopping
|
|
95
95
|
serviceStopping (service) {
|
|
96
|
-
return waitingForActiveContexts(service._trackedContexts, service.log, service.settings.$shutdownTimeout || service.broker.options.contextTracking.shutdownTimeout, service)
|
|
96
|
+
return waitingForActiveContexts(service._trackedContexts, service.log, service.settings.$shutdownTimeout || service.broker.options.contextTracking.shutdownTimeout, service);
|
|
97
97
|
},
|
|
98
98
|
|
|
99
99
|
// Before broker stopping
|
|
100
100
|
stopping () {
|
|
101
|
-
return waitingForActiveContexts(runtime.state.trackedContexts, runtime.log, runtime.options.contextTracking.shutdownTimeout)
|
|
101
|
+
return waitingForActiveContexts(runtime.state.trackedContexts, runtime.log, runtime.options.contextTracking.shutdownTimeout);
|
|
102
102
|
},
|
|
103
103
|
localAction: wrapContextTrackerMiddleware,
|
|
104
104
|
remoteAction: wrapContextTrackerMiddleware,
|
|
105
105
|
localEvent: wrapContextTrackerMiddleware
|
|
106
|
-
}
|
|
107
|
-
}
|
|
106
|
+
};
|
|
107
|
+
};
|
|
@@ -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
6
|
|
|
7
|
-
const { WeaveError } = require('../../errors')
|
|
7
|
+
const { WeaveError } = require('../../errors');
|
|
8
8
|
|
|
9
9
|
module.exports = (runtime) => {
|
|
10
10
|
const wrapErrorHandlerMiddleware = function (handler) {
|
|
@@ -12,24 +12,24 @@ module.exports = (runtime) => {
|
|
|
12
12
|
return handler(context, serviceInjections)
|
|
13
13
|
.catch(error => {
|
|
14
14
|
if (!(error instanceof Error)) {
|
|
15
|
-
error = new WeaveError(error, 500)
|
|
15
|
+
error = new WeaveError(error, 500);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
if (runtime.nodeId !== context.nodeId) {
|
|
19
|
-
runtime.transport.removePendingRequestsById(context.id)
|
|
19
|
+
runtime.transport.removePendingRequestsById(context.id);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
runtime.log.debug(`The action ${context.action.name} is rejected`, { requestId: context.id }, error)
|
|
23
|
-
return runtime.handleError(error)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
}
|
|
22
|
+
runtime.log.debug(`The action ${context.action.name} is rejected`, { requestId: context.id }, error);
|
|
23
|
+
return runtime.handleError(error);
|
|
24
|
+
});
|
|
25
|
+
// .catch(error => {
|
|
26
|
+
// runtime.log.error(error)
|
|
27
|
+
// })
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
30
|
|
|
31
31
|
return {
|
|
32
32
|
localAction: wrapErrorHandlerMiddleware,
|
|
33
33
|
remoteAction: wrapErrorHandlerMiddleware
|
|
34
|
-
}
|
|
35
|
-
}
|
|
34
|
+
};
|
|
35
|
+
};
|
package/lib/middlewares/index.js
CHANGED
|
@@ -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
|
*/
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
module.exports = {
|
|
8
8
|
ActionHooks: require('./action-hooks'),
|
|
9
9
|
Bulkhead: require('./bulkhead'),
|
|
10
|
+
Cache: require('./cache'),
|
|
10
11
|
CircuitBreaker: require('./circuit-breaker'),
|
|
11
12
|
ErrorHandler: require('./error-handler'),
|
|
12
13
|
Metrics: require('./metrics'),
|
|
@@ -15,4 +16,4 @@ module.exports = {
|
|
|
15
16
|
Timeout: require('./timeout'),
|
|
16
17
|
ContextTracker: require('./context-tracker'),
|
|
17
18
|
Validator: require('./validator')
|
|
18
|
-
}
|
|
19
|
+
};
|