@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
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('./types.js').BrokerOptions} BrokerOptions
|
|
3
|
+
* @typedef {import('./types.js').Runtime} Runtime
|
|
4
|
+
* @typedef {import('./types.js').Broker} Broker
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { initLogger } = require('./runtime/initLogger');
|
|
8
|
+
const { initMiddlewareHandler } = require('./runtime/initMiddlewareManager');
|
|
9
|
+
const { initRegistry } = require('./runtime/initRegistry');
|
|
10
|
+
const { initContextFactory } = require('./runtime/initContextFactory');
|
|
11
|
+
const { initEventbus } = require('./runtime/initEventbus');
|
|
12
|
+
const { initValidator } = require('./runtime/initValidator');
|
|
13
|
+
const { initTransport } = require('./runtime/initTransport');
|
|
14
|
+
const { initCache } = require('./runtime/initCache');
|
|
15
|
+
const { initActionInvoker } = require('./runtime/initActionInvoker');
|
|
16
|
+
const { initServiceManager } = require('./runtime/initServiceManager');
|
|
17
|
+
const { initMetrics } = require('./runtime/initMetrics');
|
|
18
|
+
const { initTracer } = require('./runtime/initTracing');
|
|
19
|
+
const { initUUIDFactory } = require('./runtime/initUuidFactory');
|
|
20
|
+
const { errorHandler, fatalErrorHandler } = require('./errorHandler');
|
|
21
|
+
const { uuid } = require('@weave-js/utils');
|
|
22
|
+
const { version } = require('../package.json');
|
|
23
|
+
const EventEmitter = require('eventemitter2');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build runtime object
|
|
27
|
+
* @param {BrokerOptions} options Broker options
|
|
28
|
+
* @return {Runtime} Runtime
|
|
29
|
+
*/
|
|
30
|
+
exports.initRuntime = (options) => {
|
|
31
|
+
/**
|
|
32
|
+
* Event bus
|
|
33
|
+
* @returns {EventEmitter} Service object.
|
|
34
|
+
*/
|
|
35
|
+
const bus = new EventEmitter({
|
|
36
|
+
wildcard: true,
|
|
37
|
+
maxListeners: 1000
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Create base runtime object
|
|
41
|
+
const runtime = {
|
|
42
|
+
nodeId: options.nodeId,
|
|
43
|
+
version,
|
|
44
|
+
options,
|
|
45
|
+
bus,
|
|
46
|
+
state: {
|
|
47
|
+
instanceId: uuid(),
|
|
48
|
+
isStarted: false
|
|
49
|
+
},
|
|
50
|
+
handleError: (error) => errorHandler(runtime, error),
|
|
51
|
+
fatalError: (message, error, killProcess) => fatalErrorHandler(runtime, message, error, killProcess)
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Init modules
|
|
55
|
+
initLogger(runtime);
|
|
56
|
+
initUUIDFactory(runtime);
|
|
57
|
+
initMiddlewareHandler(runtime);
|
|
58
|
+
initRegistry(runtime);
|
|
59
|
+
initContextFactory(runtime);
|
|
60
|
+
initEventbus(runtime);
|
|
61
|
+
initValidator(runtime);
|
|
62
|
+
initTransport(runtime);
|
|
63
|
+
initCache(runtime);
|
|
64
|
+
initActionInvoker(runtime);
|
|
65
|
+
initServiceManager(runtime);
|
|
66
|
+
initMetrics(runtime);
|
|
67
|
+
initTracer(runtime);
|
|
68
|
+
|
|
69
|
+
return runtime;
|
|
70
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Author: Kevin Ries (kevin.ries@fachwerk.io)
|
|
3
|
+
* -----
|
|
4
|
+
* Copyright 2021 Fachwerk
|
|
5
|
+
*/
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
const { isObject, isString } = require('@weave-js/utils');
|
|
8
|
+
const Constants = require('../../metrics/constants');
|
|
9
|
+
const { WeaveError } = require('../../errors');
|
|
10
|
+
const { getCacheKeyByObject } = require('../getCacheKeyByObject');
|
|
11
|
+
const { getPropertyFromDataOrMetadata } = require('../getPropertyFromDataOrMetadata');
|
|
12
|
+
|
|
13
|
+
function generateHash (key) {
|
|
14
|
+
return crypto
|
|
15
|
+
.createHash('sha1')
|
|
16
|
+
.update(key)
|
|
17
|
+
.digest('base64');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function registerCacheMetrics (metrics) {
|
|
21
|
+
metrics.register({ type: 'counter', name: Constants.CACHE_GET_TOTAL });
|
|
22
|
+
metrics.register({ type: 'counter', name: Constants.CACHE_SET_TOTAL });
|
|
23
|
+
metrics.register({ type: 'counter', name: Constants.CACHE_FOUND_TOTAL });
|
|
24
|
+
metrics.register({ type: 'counter', name: Constants.CACHE_EXPIRED_TOTAL });
|
|
25
|
+
metrics.register({ type: 'counter', name: Constants.CACHE_DELETED_TOTAL });
|
|
26
|
+
metrics.register({ type: 'counter', name: Constants.CACHE_CLEANED_TOTAL });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const createCacheBase = (name, runtime, adapterOptions, options) => {
|
|
30
|
+
if (!isString(name)) {
|
|
31
|
+
throw new WeaveError('Name must be a string.');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const cache = {
|
|
35
|
+
name,
|
|
36
|
+
isConnected: false,
|
|
37
|
+
runtime,
|
|
38
|
+
options: Object.assign({
|
|
39
|
+
ttl: null
|
|
40
|
+
}, options),
|
|
41
|
+
init () {
|
|
42
|
+
// register metrics
|
|
43
|
+
if (runtime.metrics) {
|
|
44
|
+
this.metrics = runtime.metrics;
|
|
45
|
+
registerCacheMetrics(runtime.metrics);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
log: runtime.createLogger('CACHER'),
|
|
49
|
+
set (/* hashKey, result, ttl */) {
|
|
50
|
+
/* istanbul ignore next */
|
|
51
|
+
runtime.handleError(new Error('Method not implemented.'));
|
|
52
|
+
},
|
|
53
|
+
get (/* hashKey */) {
|
|
54
|
+
/* istanbul ignore next */
|
|
55
|
+
runtime.handleError(new Error('Method not implemented.'));
|
|
56
|
+
},
|
|
57
|
+
remove () {
|
|
58
|
+
/* istanbul ignore next */
|
|
59
|
+
runtime.handleError(new Error('Method not implemented.'));
|
|
60
|
+
},
|
|
61
|
+
clear () {
|
|
62
|
+
/* istanbul ignore next */
|
|
63
|
+
runtime.handleError(new Error('Method not implemented.'));
|
|
64
|
+
},
|
|
65
|
+
stop () {
|
|
66
|
+
/* istanbul ignore next */
|
|
67
|
+
return Promise.resolve();
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* Generates a caching key for action data/metadata.
|
|
71
|
+
* @param {string} actionName Namme of the action
|
|
72
|
+
* @param {any} data Data
|
|
73
|
+
* @param {object=} metadata Metadata
|
|
74
|
+
* @param {Array<string>=} keys Key array
|
|
75
|
+
* @returns {string} Result key string
|
|
76
|
+
*/
|
|
77
|
+
getCachingKey (actionName, data, metadata, keys) {
|
|
78
|
+
if (data || metadata) {
|
|
79
|
+
const prefix = actionName + '.';
|
|
80
|
+
|
|
81
|
+
if (keys) {
|
|
82
|
+
// fast path for single keys
|
|
83
|
+
if (keys.length === 1) {
|
|
84
|
+
const cacheKeyData = getPropertyFromDataOrMetadata(data, metadata, keys[0]);
|
|
85
|
+
const key = getCacheKeyByObject(cacheKeyData);
|
|
86
|
+
const value = prefix + (isObject(cacheKeyData) ? key : cacheKeyData);
|
|
87
|
+
return prefix + generateHash(value);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Handle data cache keys
|
|
91
|
+
if (keys.length > 0) {
|
|
92
|
+
const valueString = keys.reduce((pre, property, index) => {
|
|
93
|
+
let value = getPropertyFromDataOrMetadata(data, metadata, property);
|
|
94
|
+
value = getCacheKeyByObject(value);
|
|
95
|
+
|
|
96
|
+
return pre + (index > 0 ? '|' : '') + value;
|
|
97
|
+
}, prefix);
|
|
98
|
+
|
|
99
|
+
return prefix + generateHash(valueString);
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
const value = getCacheKeyByObject(data);
|
|
103
|
+
return prefix + generateHash(value);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return actionName;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
Object.defineProperty(cache, 'adapterOptions', {
|
|
112
|
+
value: adapterOptions,
|
|
113
|
+
writable: false
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return cache;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
module.exports = { createCacheBase };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Author: Kevin Ries (kevin.ries@fachwerk.io)
|
|
3
|
+
* -----
|
|
4
|
+
* Copyright 2021 Fachwerk
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { match, defaultsDeep } = require('@weave-js/utils');
|
|
8
|
+
const { createCacheBase } = require('./base');
|
|
9
|
+
const { createLock } = require('../lock');
|
|
10
|
+
const Constants = require('../../metrics/constants');
|
|
11
|
+
|
|
12
|
+
const defaultAdapterOptions = {
|
|
13
|
+
ttlCheckInterval: 3000
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} InMemoryAdapterOptions
|
|
18
|
+
* @property {number=} ttlCheckInterval TTL check interval
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create an in-memory cache adapter.
|
|
23
|
+
* @param {InMemoryAdapterOptions} adapterOptions Adapter options
|
|
24
|
+
* @returns {any} CacheFactory
|
|
25
|
+
*/
|
|
26
|
+
const createInMemoryCache = (adapterOptions = {}) => (runtime, options = {}) => {
|
|
27
|
+
adapterOptions = defaultsDeep(adapterOptions, defaultAdapterOptions);
|
|
28
|
+
const base = createCacheBase('In-Memory', runtime, adapterOptions, options);
|
|
29
|
+
const storage = new Map();
|
|
30
|
+
|
|
31
|
+
const lock = createLock();
|
|
32
|
+
|
|
33
|
+
const ttlTimerHandle = setInterval(() => {
|
|
34
|
+
checkTtl();
|
|
35
|
+
}, adapterOptions.ttlCheckInterval);
|
|
36
|
+
|
|
37
|
+
ttlTimerHandle.unref();
|
|
38
|
+
|
|
39
|
+
// if a new broker gets connected, we need to clear the cache
|
|
40
|
+
runtime.bus.on('$transport.connected', () => {
|
|
41
|
+
base.log.debug('Transport adapter connected. Cache will be cleared.');
|
|
42
|
+
cache.clear();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const checkTtl = () => {
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
|
|
48
|
+
storage.forEach((item, hashKey) => {
|
|
49
|
+
if (item.expire && item.expire < now) {
|
|
50
|
+
cache.log.debug(`Delete ${hashKey}`);
|
|
51
|
+
storage.delete(hashKey);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const cache = Object.assign(
|
|
57
|
+
{},
|
|
58
|
+
base,
|
|
59
|
+
{
|
|
60
|
+
init () {
|
|
61
|
+
base.init();
|
|
62
|
+
cache.isConnected = true;
|
|
63
|
+
},
|
|
64
|
+
get (cacheKey) {
|
|
65
|
+
base.log.debug(`Get ${cacheKey}`);
|
|
66
|
+
|
|
67
|
+
if (base.metrics) {
|
|
68
|
+
base.metrics.increment(Constants.CACHE_GET_TOTAL);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const item = storage.get(cacheKey);
|
|
72
|
+
|
|
73
|
+
if (item) {
|
|
74
|
+
cache.log.debug(`Found ${cacheKey}`);
|
|
75
|
+
|
|
76
|
+
if (base.metrics) {
|
|
77
|
+
base.metrics.increment(Constants.CACHE_FOUND_TOTAL);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (item.expire && item.expire < Date.now()) {
|
|
81
|
+
cache.log.debug(`Delete ${cacheKey}`);
|
|
82
|
+
storage.delete(cacheKey);
|
|
83
|
+
if (base.metrics) {
|
|
84
|
+
base.metrics.increment(Constants.CACHE_EXPIRED_TOTAL);
|
|
85
|
+
}
|
|
86
|
+
return Promise.resolve(null);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return Promise.resolve(item.data);
|
|
90
|
+
}
|
|
91
|
+
return Promise.resolve(null);
|
|
92
|
+
},
|
|
93
|
+
set (hashKey, data, ttl) {
|
|
94
|
+
if (base.metrics) {
|
|
95
|
+
base.metrics.increment(Constants.CACHE_SET_TOTAL);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// if ttl is not set in action cache settings, use options ttl
|
|
99
|
+
if (ttl == null) {
|
|
100
|
+
ttl = options.ttl;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
storage.set(hashKey, {
|
|
104
|
+
data,
|
|
105
|
+
expire: ttl ? Date.now() + ttl : null
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
base.log.debug(`Set ${hashKey}`);
|
|
109
|
+
|
|
110
|
+
return Promise.resolve(data);
|
|
111
|
+
},
|
|
112
|
+
remove (hashKey) {
|
|
113
|
+
if (base.metrics) {
|
|
114
|
+
base.metrics.increment(Constants.CACHE_DELETED_TOTAL);
|
|
115
|
+
}
|
|
116
|
+
storage.delete(hashKey);
|
|
117
|
+
base.log.debug(`Delete cached object with key ${hashKey}`);
|
|
118
|
+
|
|
119
|
+
return Promise.resolve();
|
|
120
|
+
},
|
|
121
|
+
clear (pattern = '**') {
|
|
122
|
+
if (base.metrics) {
|
|
123
|
+
base.metrics.increment(Constants.CACHE_DELETED_TOTAL);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
storage.forEach((_, key) => {
|
|
127
|
+
if (match(key, pattern)) {
|
|
128
|
+
this.remove(key);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
return Promise.resolve();
|
|
132
|
+
},
|
|
133
|
+
lock (key, ttl) {
|
|
134
|
+
return lock.acquire(key, ttl).then(() => {
|
|
135
|
+
return () => lock.release(key);
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
tryAcquireLock (key, ttl) {
|
|
139
|
+
if (lock.isLocked(key)) {
|
|
140
|
+
return Promise.reject(new Error('Locked'));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return lock.acquire(key, ttl).then(() => {
|
|
144
|
+
return () => lock.release(key);
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
async stop () {
|
|
148
|
+
clearInterval(ttlTimerHandle);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return cache;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
module.exports = { createInMemoryCache };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Author: Kevin Ries (kevin.ries@fachwerk.io)
|
|
3
|
+
* -----
|
|
4
|
+
* Copyright 2021 Fachwerk
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { match } = require('@weave-js/utils');
|
|
8
|
+
const { createCacheBase } = require('./base');
|
|
9
|
+
const { createLock } = require('../lock');
|
|
10
|
+
const Constants = require('../../metrics/constants');
|
|
11
|
+
|
|
12
|
+
const createInMemoryLruCache = (adapterOptions) => (runtime, options = {}) => {
|
|
13
|
+
const name = 'In-Memory';
|
|
14
|
+
const base = createCacheBase(name, runtime, options);
|
|
15
|
+
const storage = new Map();
|
|
16
|
+
|
|
17
|
+
const lock = createLock();
|
|
18
|
+
|
|
19
|
+
const timer = setInterval(() => {
|
|
20
|
+
checkTtl();
|
|
21
|
+
}, 3000);
|
|
22
|
+
|
|
23
|
+
timer.unref();
|
|
24
|
+
|
|
25
|
+
// if a new broker gets connected, we need to clear the cache
|
|
26
|
+
runtime.bus.on('$transport.connected', () => {
|
|
27
|
+
base.log.debug('Transport adapter connected. Cache will be cleared.');
|
|
28
|
+
cache.clear();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const checkTtl = () => {
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
|
|
34
|
+
storage.forEach((item, hashKey) => {
|
|
35
|
+
if (item.expire && item.expire < now) {
|
|
36
|
+
cache.log.debug(`Delete ${hashKey}`);
|
|
37
|
+
storage.delete(hashKey);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const cache = Object.assign(
|
|
43
|
+
{},
|
|
44
|
+
base,
|
|
45
|
+
{
|
|
46
|
+
name,
|
|
47
|
+
init () {
|
|
48
|
+
base.init();
|
|
49
|
+
cache.isConnected = true;
|
|
50
|
+
},
|
|
51
|
+
get (cacheKey) {
|
|
52
|
+
base.log.debug(`Get ${cacheKey}`);
|
|
53
|
+
|
|
54
|
+
if (base.metrics) {
|
|
55
|
+
base.metrics.increment(Constants.CACHE_GET_TOTAL);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const item = storage.get(cacheKey);
|
|
59
|
+
|
|
60
|
+
if (item) {
|
|
61
|
+
cache.log.debug(`Found ${cacheKey}`);
|
|
62
|
+
|
|
63
|
+
if (base.metrics) {
|
|
64
|
+
base.metrics.increment(Constants.CACHE_FOUND_TOTAL);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (item.expire && item.expire < Date.now()) {
|
|
68
|
+
cache.log.debug(`Delete ${cacheKey}`);
|
|
69
|
+
storage.delete(cacheKey);
|
|
70
|
+
if (base.metrics) {
|
|
71
|
+
base.metrics.increment(Constants.CACHE_EXPIRED_TOTAL);
|
|
72
|
+
}
|
|
73
|
+
return Promise.resolve(null);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return Promise.resolve(item.data);
|
|
77
|
+
}
|
|
78
|
+
return Promise.resolve(null);
|
|
79
|
+
},
|
|
80
|
+
set (hashKey, data, ttl) {
|
|
81
|
+
if (base.metrics) {
|
|
82
|
+
base.metrics.increment(Constants.CACHE_SET_TOTAL);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (ttl == null) {
|
|
86
|
+
ttl = options.ttl;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
storage.set(hashKey, {
|
|
90
|
+
data,
|
|
91
|
+
expire: ttl ? Date.now() + ttl : null
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
base.log.debug(`Set ${hashKey}`);
|
|
95
|
+
|
|
96
|
+
return Promise.resolve(data);
|
|
97
|
+
},
|
|
98
|
+
remove (hashKey) {
|
|
99
|
+
if (base.metrics) {
|
|
100
|
+
base.metrics.increment(Constants.CACHE_DELETED_TOTAL);
|
|
101
|
+
}
|
|
102
|
+
storage.delete(hashKey);
|
|
103
|
+
base.log.debug(`Delete ${hashKey}`);
|
|
104
|
+
|
|
105
|
+
return Promise.resolve();
|
|
106
|
+
},
|
|
107
|
+
clear (pattern = '**') {
|
|
108
|
+
if (base.metrics) {
|
|
109
|
+
base.metrics.increment(Constants.CACHE_DELETED_TOTAL);
|
|
110
|
+
}
|
|
111
|
+
storage.forEach((_, key) => {
|
|
112
|
+
if (match(key, pattern)) {
|
|
113
|
+
base.log.debug(`Delete ${key}`);
|
|
114
|
+
this.remove(key);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return Promise.resolve();
|
|
118
|
+
},
|
|
119
|
+
lock (key, ttl) {
|
|
120
|
+
return lock.acquire(key, ttl).then(() => {
|
|
121
|
+
return () => lock.release(key);
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
tryAcquireLock (key, ttl) {
|
|
125
|
+
if (lock.isLocked(key)) {
|
|
126
|
+
return Promise.reject(new Error('Locked'));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return lock.acquire(key, ttl).then(() => {
|
|
130
|
+
return () => lock.release(key);
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
stop () {
|
|
134
|
+
clearInterval(timer);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
return cache;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
module.exports = { createInMemoryLruCache };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const { isObject, isString } = require('@weave-js/utils');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get property from data or metadata object.
|
|
5
|
+
* @param {any} value Value
|
|
6
|
+
* @returns {string} Cache key
|
|
7
|
+
*/
|
|
8
|
+
const getCacheKeyByObject = (value) => {
|
|
9
|
+
if (Array.isArray(value)) {
|
|
10
|
+
return '[' + value.map(object => getCacheKeyByObject(object)).join(',') + ']';
|
|
11
|
+
} else if (isObject(value)) {
|
|
12
|
+
// Handle date objects
|
|
13
|
+
if (value instanceof Date) {
|
|
14
|
+
return value.toISOString();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return '{' + Object.keys(value)
|
|
18
|
+
.map((key) => {
|
|
19
|
+
return [key, getCacheKeyByObject(value[key])].join(':');
|
|
20
|
+
})
|
|
21
|
+
.join(',') + '}';
|
|
22
|
+
} else if (isString(value)) {
|
|
23
|
+
return value;
|
|
24
|
+
} else if (typeof value === 'boolean' || typeof value === 'number') {
|
|
25
|
+
return value.toString();
|
|
26
|
+
} else {
|
|
27
|
+
return 'null';
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
module.exports = { getCacheKeyByObject };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const { dotGet } = require('@weave-js/utils');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get property from data or metadata object.
|
|
5
|
+
* @param {any} data data object
|
|
6
|
+
* @param {object} metadata metadata object
|
|
7
|
+
* @param {string} key key
|
|
8
|
+
* @returns {any} Result
|
|
9
|
+
*/
|
|
10
|
+
const getPropertyFromDataOrMetadata = (data, metadata, key) => {
|
|
11
|
+
// if a key starts with ":", the property is picked from metadata
|
|
12
|
+
if (key.startsWith(':')) {
|
|
13
|
+
// remove ':' from key.
|
|
14
|
+
key = key.replace(':', '');
|
|
15
|
+
return dotGet(metadata, key);
|
|
16
|
+
}
|
|
17
|
+
return dotGet(data, key);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
module.exports = { getPropertyFromDataOrMetadata };
|
package/lib/cache/lock.js
CHANGED
|
@@ -1,14 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
const locked = new Map()
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
const createLock = () => {
|
|
2
|
+
const locked = new Map();
|
|
3
|
+
|
|
4
|
+
function acquire (key, ttl) {
|
|
5
|
+
const lockedItems = locked.get(key);
|
|
6
|
+
if (!lockedItems) {
|
|
7
|
+
locked.set(key, []);
|
|
8
|
+
return Promise.resolve();
|
|
9
|
+
} else {
|
|
10
|
+
return new Promise((resolve) => lockedItems.push(resolve));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isLocked (key) {
|
|
15
|
+
return !!locked.has(key);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function release (key) {
|
|
19
|
+
const lockedItems = locked.get(key);
|
|
20
|
+
if (lockedItems) {
|
|
21
|
+
if (lockedItems.length > 0) {
|
|
22
|
+
lockedItems.shift()();
|
|
23
|
+
} else {
|
|
24
|
+
locked.delete(key);
|
|
8
25
|
}
|
|
9
|
-
},
|
|
10
|
-
isLocked (key) {
|
|
11
|
-
return !!locked.has(key)
|
|
12
26
|
}
|
|
27
|
+
return Promise.resolve();
|
|
13
28
|
}
|
|
14
|
-
|
|
29
|
+
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
acquire,
|
|
32
|
+
isLocked,
|
|
33
|
+
release
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = { createLock };
|
package/lib/constants.js
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
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
|
-
exports.PROTOCOL_VERSION = 1
|
|
7
|
+
exports.PROTOCOL_VERSION = 1;
|
|
8
8
|
|
|
9
9
|
exports.loadBalancingStrategy = {
|
|
10
10
|
ROUND_ROBIN: 'round_robin',
|
|
11
11
|
RANDOM: 'random'
|
|
12
|
-
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
exports.circuitBreakerStates = {
|
|
15
|
+
CIRCUIT_CLOSED: 'closed',
|
|
16
|
+
CIRCUIT_HALF_OPENED: 'half_opened',
|
|
17
|
+
CIRCUIT_HALF_OPEN_WAITING: 'half_open_waiting',
|
|
18
|
+
CIRCUIT_OPENED: 'opened'
|
|
19
|
+
};
|
|
13
20
|
|
|
14
21
|
exports.level = {
|
|
15
22
|
trace: 'trace',
|
|
@@ -18,8 +25,4 @@ exports.level = {
|
|
|
18
25
|
warn: 'warn',
|
|
19
26
|
error: 'error',
|
|
20
27
|
fatal: 'fatal'
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
exports.SYNC_MIDDLEWARE_HOOKS = {
|
|
24
|
-
STARTING: 'starting'
|
|
25
|
-
}
|
|
28
|
+
};
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
exports.errorHandler = ({ options }, error) => {
|
|
2
2
|
if (options.errorHandler) {
|
|
3
|
-
return options.errorHandler.call(null, error)
|
|
3
|
+
return options.errorHandler.call(null, error);
|
|
4
4
|
}
|
|
5
|
-
throw error
|
|
6
|
-
}
|
|
5
|
+
throw error;
|
|
6
|
+
};
|
|
7
7
|
|
|
8
8
|
exports.fatalErrorHandler = (runtime, message, error, killProcess = true) => {
|
|
9
|
-
const { options, log } = runtime
|
|
9
|
+
const { options, log } = runtime;
|
|
10
10
|
if (options.logger.enabled) {
|
|
11
|
-
log.fatal(error, message)
|
|
11
|
+
log.fatal(error, message);
|
|
12
12
|
} else {
|
|
13
|
-
console.error(message, error)
|
|
13
|
+
console.error(message, error);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
if (killProcess) {
|
|
17
|
-
process.exit(1)
|
|
17
|
+
process.exit(1);
|
|
18
18
|
}
|
|
19
|
-
}
|
|
19
|
+
};
|