@weave-js/core 0.9.4 → 0.11.1

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.
Files changed (98) hide show
  1. package/lib/ExtendableError.js +33 -0
  2. package/lib/broker/context.js +25 -13
  3. package/lib/broker/{default-options.js → defaultOptions.js} +25 -6
  4. package/lib/broker/index.js +224 -194
  5. package/lib/buildRuntime.js +70 -0
  6. package/lib/cache/adapters/base.js +143 -0
  7. package/lib/cache/adapters/inMemory.js +154 -0
  8. package/lib/cache/adapters/inMemoryLru.js +140 -0
  9. package/lib/cache/adapters/index.js +46 -0
  10. package/lib/cache/base.js +49 -44
  11. package/lib/cache/lock.js +21 -4
  12. package/lib/constants.js +8 -5
  13. package/lib/errorHandler.js +19 -0
  14. package/lib/errors.js +17 -10
  15. package/lib/helper/{define-action.js → defineAction.js} +3 -1
  16. package/lib/helper/{define-broker-options.js → defineBrokerOptions.js} +2 -0
  17. package/lib/helper/{define-service.js → defineService.js} +4 -1
  18. package/lib/index.js +17 -92
  19. package/lib/logger/format/asHumanReadable.js +4 -3
  20. package/lib/logger/index.js +7 -1
  21. package/lib/logger/levels.js +8 -7
  22. package/lib/logger/tools.js +4 -0
  23. package/lib/logger/utils/format.js +18 -4
  24. package/lib/metrics/constants.js +8 -6
  25. package/lib/metrics/types/gauge.js +1 -1
  26. package/lib/middlewares/action-hooks/index.js +62 -14
  27. package/lib/middlewares/bulkhead/index.js +5 -4
  28. package/lib/middlewares/cache/index.js +106 -0
  29. package/lib/middlewares/circuit-breaker/index.js +41 -16
  30. package/lib/middlewares/context-tracker/index.js +3 -3
  31. package/lib/middlewares/error-handler/index.js +3 -3
  32. package/lib/middlewares/index.js +2 -1
  33. package/lib/middlewares/metrics/getMiddlewareWrapper.js +2 -2
  34. package/lib/middlewares/metrics/index.js +2 -2
  35. package/lib/middlewares/retry/index.js +11 -10
  36. package/lib/middlewares/timeout/index.js +5 -5
  37. package/lib/middlewares/tracing/index.js +27 -14
  38. package/lib/middlewares/tracing/tags.js +43 -0
  39. package/lib/middlewares/validator/index.js +6 -6
  40. package/lib/registry/{action-endpoint.js → actionEndpoint.js} +0 -0
  41. package/lib/registry/collections/{action-collection.js → actionCollection.js} +2 -2
  42. package/lib/registry/collections/{endpoint-collection.js → endpointCollection.js} +19 -3
  43. package/lib/registry/collections/{event-collection.js → eventCollection.js} +15 -9
  44. package/lib/registry/collections/{node-collection.js → nodeCollection.js} +1 -1
  45. package/lib/registry/collections/{service-collection.js → serviceCollection.js} +2 -2
  46. package/lib/registry/{event-endpoint.js → eventEndpoint.js} +1 -1
  47. package/lib/registry/node.js +1 -1
  48. package/lib/registry/registry.js +19 -38
  49. package/lib/registry/service/service.js +2 -2
  50. package/lib/registry/{service-item.js → serviceItem.js} +1 -1
  51. package/lib/runtime/{init-action-invoker.js → initActionInvoker.js} +3 -3
  52. package/lib/runtime/initCache.js +19 -0
  53. package/lib/runtime/{init-context-factory.js → initContextFactory.js} +2 -2
  54. package/lib/runtime/{init-eventbus.js → initEventbus.js} +11 -10
  55. package/lib/runtime/{init-logger.js → initLogger.js} +0 -0
  56. package/lib/runtime/{init-metrics.js → initMetrics.js} +11 -10
  57. package/lib/runtime/{init-middleware-manager.js → initMiddlewareManager.js} +1 -1
  58. package/lib/runtime/{init-registry.js → initRegistry.js} +1 -1
  59. package/lib/runtime/{init-service-manager.js → initServiceManager.js} +0 -0
  60. package/lib/runtime/{init-tracing.js → initTracing.js} +2 -1
  61. package/lib/runtime/{init-transport.js → initTransport.js} +0 -0
  62. package/lib/runtime/{init-uuid-factory.js → initUuidFactory.js} +0 -0
  63. package/lib/runtime/{init-validator.js → initValidator.js} +1 -1
  64. package/lib/tracing/collectors/base.js +11 -19
  65. package/lib/tracing/collectors/event.js +2 -2
  66. package/lib/tracing/collectors/index.js +2 -2
  67. package/lib/tracing/span.js +8 -6
  68. package/lib/transport/InboundTransformStream.js +75 -0
  69. package/lib/transport/adapters/{adapter-base.js → adapteBase.js} +16 -1
  70. package/lib/transport/adapters/adapters.js +1 -1
  71. package/lib/transport/adapters/dummy/index.js +3 -3
  72. package/lib/transport/adapters/index.js +1 -1
  73. package/lib/transport/adapters/tcp/discovery/index.js +45 -31
  74. package/lib/transport/adapters/tcp/index.js +20 -13
  75. package/lib/transport/adapters/tcp/tcpReader.js +1 -0
  76. package/lib/transport/adapters/tcp/tcpWriteStream.js +1 -1
  77. package/lib/transport/adapters/tcp/tcpWriter.js +2 -2
  78. package/lib/transport/createMessage.js +11 -0
  79. package/lib/transport/createTransport.js +215 -172
  80. package/lib/transport/errorPayloadFactory.js +18 -0
  81. package/lib/transport/{message-handlers.js → messageHandlers.js} +92 -23
  82. package/lib/transport/messageTypes.js +23 -0
  83. package/lib/types.js +86 -75
  84. package/lib/utils/index.js +1 -1
  85. package/lib/utils/options.js +4 -2
  86. package/lib/utils/{restore-error.js → restoreError.js} +1 -1
  87. package/package.json +6 -12
  88. package/CHANGELOG.md +0 -1317
  89. package/lib/cache/index.js +0 -48
  90. package/lib/cache/memory.js +0 -91
  91. package/lib/middlewares/tracing/buildTags.js +0 -17
  92. package/lib/new-errors.js +0 -25
  93. package/lib/runtime/init-cache.js +0 -10
  94. package/lib/tracing/collectors/console.js +0 -34
  95. package/lib/tracing/index.js +0 -3
  96. package/lib/tracing/tracer.js +0 -64
  97. package/lib/transport/message-types.js +0 -59
  98. package/lib/transport/message.js +0 -16
@@ -0,0 +1,75 @@
1
+ const { Transform } = require('stream')
2
+
3
+ const pushWithBackpressure = (stream, chunks, encoding, callback = null, $index = 0) => {
4
+ if (!(stream instanceof Transform)) {
5
+ throw new TypeError('Argument "stream" must be an instance of Duplex')
6
+ }
7
+
8
+ chunks = [].concat(chunks).filter(x => x !== undefined)
9
+
10
+ if (typeof encoding === 'function') {
11
+ callback = encoding
12
+ encoding = undefined
13
+ }
14
+
15
+ if ($index >= chunks.length) {
16
+ if (typeof callback === 'function') {
17
+ callback()
18
+ }
19
+ return stream
20
+ } else if (!stream.push(chunks[$index], ...([encoding].filter(Boolean)))) {
21
+ stream.emit('backpressure', {
22
+ sender: stream.sender,
23
+ requestId: stream.requestId
24
+ })
25
+
26
+ const pipedStreams = [].concat(
27
+ (stream._readableState || {}).pipes || stream
28
+ ).filter(Boolean)
29
+
30
+ let listenerCalled = false
31
+
32
+ const drainListener = () => {
33
+ stream.emit('resume_backpressure', {
34
+ sender: stream.sender,
35
+ requestId: stream.requestId
36
+ })
37
+
38
+ if (listenerCalled) {
39
+ return
40
+ }
41
+
42
+ listenerCalled = true
43
+
44
+ for (const stream of pipedStreams) {
45
+ stream.removeListener('drain', drainListener)
46
+ }
47
+
48
+ pushWithBackpressure(stream, chunks, encoding, callback, $index + 1)
49
+ }
50
+
51
+ for (const stream of pipedStreams) {
52
+ stream.once('drain', drainListener)
53
+ }
54
+
55
+ return stream
56
+ }
57
+ return pushWithBackpressure(stream, chunks, encoding, callback, $index + 1)
58
+ }
59
+ class InboundTransformStream extends Transform {
60
+ constructor (sender, requestId, options) {
61
+ super(options)
62
+ this.sender = sender
63
+ this.requestId = requestId
64
+ }
65
+
66
+ _transform (chunk, encoding, callback) {
67
+ pushWithBackpressure(this, chunk, encoding, callback)
68
+ }
69
+
70
+ _flush (callback) {
71
+ return callback()
72
+ }
73
+ }
74
+
75
+ module.exports = { InboundTransformStream }
@@ -1,11 +1,26 @@
1
1
  /*
2
2
  * Author: Kevin Ries (kevin@fachw3rk.de)
3
3
  * -----
4
- * Copyright 2020 Fachwerk
4
+ * Copyright 2021 Fachwerk
5
5
  */
6
6
 
7
7
  const EventEmitter = require('events').EventEmitter
8
8
 
9
+ /**
10
+ * @typedef {Object} AdapterBase
11
+ * @property {string} name Name
12
+ * @property {EventEmitter} bus Event bus.
13
+ * @property {function():Promise<any>} afterInit After init callback.
14
+ * @property {boolean} isConnected Is connected state.
15
+ * @property {number} interruptCounter Interruption counter.
16
+ * @property {number} repeatAttemptCounter Repeat attempt counter
17
+ * @property {function(Object, Object, Object):Promise<any>} init Repeat attempt counter
18
+ */
19
+
20
+ /**
21
+ * Create a adapter base object.
22
+ * @returns {AdapterBase} Adapter base object
23
+ */
9
24
  const createTransportBase = () => {
10
25
  let prefix = 'weave'
11
26
 
@@ -4,6 +4,6 @@
4
4
  * Copyright 2019 Fachwerk
5
5
  */
6
6
 
7
- exports.BaseAdapter = require('./adapter-base')
7
+ exports.BaseAdapter = require('./adapteBase')
8
8
  exports.Dummy = require('./dummy')
9
9
  exports.TCP = require('./tcp')
@@ -1,10 +1,10 @@
1
1
  /*
2
2
  * Author: Kevin Ries (kevin@fachw3rk.de)
3
3
  * -----
4
- * Copyright 2020 Fachwerk
4
+ * Copyright 2021 Fachwerk
5
5
  */
6
6
 
7
- const TransportBase = require('../adapter-base')
7
+ const TransportBase = require('../adapteBase')
8
8
  const EventEmitter = require('eventemitter2').EventEmitter2
9
9
 
10
10
  // create a global eventbus to pass messages between weave service brokers.
@@ -18,7 +18,7 @@ const DummyTransportAdapter = (adapterOptions) => {
18
18
 
19
19
  return Object.assign(TransportBase(adapterOptions), {
20
20
  name: 'Dummy',
21
- connect (isTryReconnect = false) {
21
+ connect () {
22
22
  this.bus.emit('$adapter.connected', false)
23
23
  this.log.info('Dummy transport client connected.')
24
24
  return Promise.resolve()
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Author: Kevin Ries (kevin@fachw3rk.de)
3
3
  * -----
4
- * Copyright 2020 Fachwerk
4
+ * Copyright 2021 Fachwerk
5
5
  */
6
6
 
7
7
  const { WeaveBrokerOptionsError } = require('../../errors')
@@ -12,7 +12,7 @@ const createDiscoveryService = (adapter, options) => {
12
12
  const codec = Codec(options)
13
13
  const bus = new EventEmitter()
14
14
  const ips = getIpList(false)
15
- const MESSAGE_TYPE_LENGHT = 1
15
+ const MESSAGE_TYPE_LENGTH = 1
16
16
 
17
17
  let servers = []
18
18
  let discoverTimer
@@ -20,32 +20,29 @@ const createDiscoveryService = (adapter, options) => {
20
20
  const startServer = (host, port, multicastAddress) => {
21
21
  return new Promise((resolve, reject) => {
22
22
  try {
23
- const socket = dgram.createSocket({ type: options.discovery.type, reuseAddr: options.discovery.udpReuseAddress })
24
-
25
- socket.on('message', (message, info) => {
26
- const messageType = Buffer.prototype.readUInt8.call(message, 0)
27
- const payload = message.slice(MESSAGE_TYPE_LENGHT)
28
-
29
- switch (messageType) {
30
- case messageTypes.HELLO:
31
- onMessage(payload, info)
32
- break
33
- default:
34
- onUnknown(payload, info)
35
- }
23
+ const socket = dgram.createSocket({
24
+ type: options.discovery.type,
25
+ reuseAddr: options.discovery.udpReuseAddress
36
26
  })
37
27
 
38
- socket.bind({ port, host, exclusive: true }, () => {
28
+ socket.on('message', onMessage)
29
+
30
+ socket.bind({ port, exclusive: true }, () => {
39
31
  try {
40
32
  if (multicastAddress) {
41
33
  socket.setMulticastInterface(host)
42
34
  socket.addMembership(multicastAddress, host)
43
35
  socket.setMulticastTTL(1)
44
- socket.destinations = [multicastAddress]
36
+ // Add destination to socket object
37
+ Object.defineProperty(socket, 'destinations', {
38
+ value: [multicastAddress]
39
+ })
45
40
  adapter.log.info(`UDP Server is listening on ${host}:${port}. Membership: ${multicastAddress}`)
46
41
  } else {
47
42
  socket.setBroadcast(true)
48
- socket.destinations = getBroadcastAddresses()
43
+ Object.defineProperty(socket, 'destinations', {
44
+ value: getBroadcastAddresses()
45
+ })
49
46
  }
50
47
  } catch (error) {
51
48
  adapter.log.info(`UDP Multicast membership error: ${error.message}`)
@@ -62,20 +59,25 @@ const createDiscoveryService = (adapter, options) => {
62
59
  }
63
60
 
64
61
  const onMessage = (buffer, info) => {
65
- const message = codec.decode(buffer)
66
- message.host = info.address
62
+ const messageType = Buffer.prototype.readUInt8.call(buffer, 0)
63
+ const payload = buffer.slice(MESSAGE_TYPE_LENGTH)
67
64
 
68
- if (message.namespace === namespace) {
69
- bus.emit('message', message)
70
- }
71
- }
65
+ switch (messageType) {
66
+ case messageTypes.HELLO:
67
+ const message = codec.decode(payload)
68
+ message.host = info.address
72
69
 
73
- const onUnknown = (message, info) => {
74
- adapter.log.debug(`Received an unknown data package from host "${info.address}"`)
70
+ if (message.namespace === namespace) {
71
+ bus.emit('message', message)
72
+ }
73
+ break
74
+ default:
75
+ adapter.log.debug(`Received an unknown data package from host "${info.address}"`)
76
+ }
75
77
  }
76
78
 
77
79
  function sendMessage (payload) {
78
- const header = Buffer.alloc(MESSAGE_TYPE_LENGHT)
80
+ const header = Buffer.alloc(MESSAGE_TYPE_LENGTH)
79
81
  Buffer.prototype.writeUInt8.call(header, messageTypes.HELLO, 0)
80
82
 
81
83
  const message = Buffer.concat([header, codec.encode(payload)])
@@ -101,7 +103,6 @@ const createDiscoveryService = (adapter, options) => {
101
103
 
102
104
  function startDiscovering (port) {
103
105
  discoverTimer = setInterval(() => sendDiscoveryPackage(port), 2000)
104
-
105
106
  discoverTimer.unref()
106
107
  }
107
108
 
@@ -115,14 +116,27 @@ const createDiscoveryService = (adapter, options) => {
115
116
 
116
117
  return {
117
118
  bus,
118
- init (port) {
119
+ async start (port) {
119
120
  if (!options.discovery.enabled) {
120
121
  return Promise.resolve()
121
122
  }
122
123
 
123
- return Promise.all(ips.map(ip => startServer(ip, options.discovery.port, options.discovery.multicastAddress)))
124
- .then(() => setTimeout(() => sendDiscoveryPackage(port), Math.floor(Math.random() * 500) + 1))
125
- .then(() => startDiscovering(port))
124
+ // UDP Multicast
125
+ if (options.discovery.udpMulticast) {
126
+ if (options.discovery.multicastAddress) {
127
+ await Promise.all(ips.map(ip => startServer(ip, options.discovery.port, options.discovery.multicastAddress)))
128
+ }
129
+ }
130
+
131
+ // UDP Broadcast
132
+ if (options.discovery.udpBroadcast) {
133
+ await startServer(options.discovery.udpBindAddress, options.discovery.port, options.discovery.multicastAddress)
134
+ }
135
+
136
+ // send the first dis
137
+ const timeout = Math.floor(Math.random() * 500) + 500
138
+ setTimeout(() => sendDiscoveryPackage(port), timeout)
139
+ startDiscovering(port)
126
140
  },
127
141
  close () {
128
142
  stopDiscovery()
@@ -1,10 +1,12 @@
1
1
 
2
2
  const { defaultsDeep } = require('@weave-js/utils')
3
- const TransportBase = require('../adapter-base')
3
+ const TransportBase = require('../adapteBase')
4
4
  const Swim = require('./discovery/index')
5
- const MessageTypes = require('../../message-types')
5
+ const MessageTypes = require('../../messageTypes')
6
6
  const TCPReader = require('./tcpReader')
7
7
  const TCPWriter = require('./tcpWriter')
8
+ const { createMessage } = require('../../createMessage')
9
+
8
10
  // const TCPMessageTypeHelper = require('./tcp-messagetypes')
9
11
 
10
12
  const defaultOptions = {
@@ -12,6 +14,7 @@ const defaultOptions = {
12
14
  discovery: {
13
15
  enabled: true,
14
16
  type: 'udp4',
17
+ udpMulticast: true,
15
18
  multicastAddress: '239.0.0.0',
16
19
  port: 54355,
17
20
  udpReuseAddress: true
@@ -59,7 +62,11 @@ module.exports = function SwimTransport (adapterOptions) {
59
62
  MessageTypes.MESSAGE_RESPONSE,
60
63
  MessageTypes.MESSAGE_GOSSIP_HELLO,
61
64
  MessageTypes.MESSAGE_GOSSIP_REQUEST,
62
- MessageTypes.MESSAGE_GOSSIP_RESPONSE
65
+ MessageTypes.MESSAGE_GOSSIP_RESPONSE,
66
+ MessageTypes.MESSAGE_RESPONSE_STREAM_BACKPRESSURE,
67
+ MessageTypes.MESSAGE_RESPONSE_STREAM_RESUME,
68
+ MessageTypes.MESSAGE_REQUEST_STREAM_BACKPRESSURE,
69
+ MessageTypes.MESSAGE_REQUEST_STREAM_RESUME
63
70
  ].includes(message.type)) {
64
71
  if (message.type === MessageTypes.MESSAGE_DISCONNECT) {
65
72
  // send a disconnect message to all connected nodes
@@ -81,7 +88,7 @@ module.exports = function SwimTransport (adapterOptions) {
81
88
 
82
89
  const localNode = self.broker.registry.nodeCollection.localNode
83
90
 
84
- const message = self.transport.createMessage(MessageTypes.MESSAGE_GOSSIP_HELLO, nodeId, {
91
+ const message = createMessage(MessageTypes.MESSAGE_GOSSIP_HELLO, nodeId, {
85
92
  host: localNode.IPList[0],
86
93
  port: localNode.port
87
94
  })
@@ -165,7 +172,7 @@ module.exports = function SwimTransport (adapterOptions) {
165
172
  }
166
173
  })
167
174
 
168
- self.swim.init(port)
175
+ self.swim.start(port)
169
176
  }
170
177
 
171
178
  function startTCPServer () {
@@ -174,19 +181,19 @@ module.exports = function SwimTransport (adapterOptions) {
174
181
 
175
182
  tcpReader.on('message', onMessage)
176
183
 
177
- tcpWriter.on('error', (error, nodeID) => {
184
+ tcpWriter.on('error', (error, nodeId) => {
178
185
  self.log.debug('TCP client error on ', error)
179
- self.broker.registry.nodeDisconnected(nodeID, false)
186
+ self.broker.registry.nodeDisconnected(nodeId, false)
180
187
  })
181
188
 
182
- tcpWriter.on('end', nodeID => {
189
+ tcpWriter.on('end', nodeId => {
183
190
  self.log.debug('TCP connection ended with')
184
- self.broker.registry.nodeDisconnected(nodeID, false)
191
+ self.broker.registry.nodeDisconnected(nodeId, false)
185
192
  })
186
193
 
187
- // tcpWriter.on('error', (_, nodeID) => {
194
+ // tcpWriter.on('error', (_, nodeId) => {
188
195
  // self.log.debug('TCP server error on ')
189
- // // this.nodes.disconnected(nodeID, false)
196
+ // // this.nodes.disconnected(nodeId, false)
190
197
  // })
191
198
 
192
199
  return tcpReader.listen()
@@ -250,7 +257,7 @@ module.exports = function SwimTransport (adapterOptions) {
250
257
 
251
258
  const destinationNode = nodes[Math.floor(Math.random() * nodes.length)]
252
259
  if (destinationNode) {
253
- const message = self.transport.createMessage(MessageTypes.MESSAGE_GOSSIP_REQUEST, destinationNode.id, payload)
260
+ const message = createMessage(MessageTypes.MESSAGE_GOSSIP_REQUEST, destinationNode.id, payload)
254
261
 
255
262
  self.send(message).catch(() => {
256
263
  self.log.debug(`Unable to send gossip response to ${destinationNode.id}`)
@@ -357,7 +364,7 @@ module.exports = function SwimTransport (adapterOptions) {
357
364
 
358
365
  if (response.online || response.offline) {
359
366
  const destinationNode = self.broker.registry.nodeCollection.get(payload.sender)
360
- const message = self.transport.createMessage(MessageTypes.MESSAGE_GOSSIP_RESPONSE, destinationNode.id, response)
367
+ const message = createMessage(MessageTypes.MESSAGE_GOSSIP_RESPONSE, destinationNode.id, response)
361
368
  self.send(message).catch(() => {})
362
369
  }
363
370
  } catch (error) {
@@ -29,6 +29,7 @@ module.exports = (adapter, options) => {
29
29
 
30
30
  self.close = () => {
31
31
  if (server && self.isConnected) {
32
+ server.close()
32
33
  sockets.forEach(socket => socket.destroy())
33
34
  sockets = []
34
35
  }
@@ -1,6 +1,6 @@
1
1
 
2
2
  const { Writable } = require('stream')
3
- const MessageTypes = require('../../message-types')
3
+ const MessageTypes = require('../../messageTypes')
4
4
  const TCPMessageTypeHelper = require('./tcp-messagetypes')
5
5
 
6
6
  module.exports = class TCPWriteStream extends Writable {
@@ -1,9 +1,9 @@
1
1
  const net = require('net')
2
2
  const { EventEmitter } = require('events')
3
- const MessageTypes = require('../../message-types')
3
+ const MessageTypes = require('../../messageTypes')
4
4
  const TCPMessageTypeHelper = require('./tcp-messagetypes')
5
5
 
6
- module.exports = (adapter, options) => {
6
+ module.exports = (adapter) => {
7
7
  const self = Object.assign({}, EventEmitter.prototype)
8
8
  const sockets = new Map()
9
9
  const messageTypeHelper = TCPMessageTypeHelper(MessageTypes)
@@ -0,0 +1,11 @@
1
+ const MessageTypes = require('./messageTypes')
2
+
3
+ const createMessage = (type, targetNodeId, payload) => {
4
+ return {
5
+ type: type || MessageTypes.MESSAGE_UNKNOWN,
6
+ targetNodeId,
7
+ payload: payload || {}
8
+ }
9
+ }
10
+
11
+ module.exports = { createMessage }