aggro 0.0.3 → 0.0.4

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -5
  3. data/aggro.gemspec +1 -3
  4. data/lib/aggro.rb +25 -7
  5. data/lib/aggro/aggregate.rb +33 -6
  6. data/lib/aggro/attribute_dsl.rb +8 -0
  7. data/lib/aggro/client.rb +4 -0
  8. data/lib/aggro/cluster_config.rb +5 -0
  9. data/lib/aggro/concurrent_actor.rb +1 -1
  10. data/lib/aggro/event_bus.rb +5 -2
  11. data/lib/aggro/event_serializer.rb +2 -2
  12. data/lib/aggro/file_store.rb +2 -2
  13. data/lib/aggro/file_store/reader.rb +11 -3
  14. data/lib/aggro/file_store/writer.rb +2 -2
  15. data/lib/aggro/handler/query.rb +19 -7
  16. data/lib/aggro/logging.rb +16 -0
  17. data/lib/aggro/marshal_stream.rb +123 -0
  18. data/lib/aggro/message/events.rb +6 -2
  19. data/lib/aggro/message/server_error.rb +20 -0
  20. data/lib/aggro/node.rb +3 -1
  21. data/lib/aggro/projection.rb +1 -0
  22. data/lib/aggro/server.rb +1 -5
  23. data/lib/aggro/subscription.rb +16 -2
  24. data/lib/aggro/transform/boolean.rb +14 -2
  25. data/lib/aggro/transform/date.rb +22 -0
  26. data/lib/aggro/transform/string.rb +2 -2
  27. data/lib/aggro/transform/time.rb +22 -0
  28. data/lib/aggro/version.rb +1 -1
  29. data/lib/aggro/zeromq_transport.rb +44 -0
  30. data/lib/aggro/{nanomsg_transport → zeromq_transport}/client.rb +11 -9
  31. data/lib/aggro/{nanomsg_transport → zeromq_transport}/publisher.rb +11 -8
  32. data/lib/aggro/zeromq_transport/server.rb +92 -0
  33. data/lib/aggro/{nanomsg_transport → zeromq_transport}/subscriber.rb +23 -24
  34. data/spec/lib/aggro/event_serializer_spec.rb +1 -1
  35. data/spec/lib/aggro/file_store/reader_spec.rb +2 -1
  36. data/spec/lib/aggro/file_store/writer_spec.rb +10 -7
  37. data/spec/lib/aggro/local_node_spec.rb +2 -2
  38. data/spec/lib/aggro/marshal_stream_spec.rb +17 -0
  39. data/spec/lib/aggro/message/events_spec.rb +4 -3
  40. data/spec/lib/aggro/node_spec.rb +3 -3
  41. data/spec/lib/aggro/subscription_spec.rb +4 -2
  42. data/spec/lib/aggro/{nanomsg_transport_spec.rb → zeromq_transport_spec.rb} +11 -7
  43. data/spec/spec_helper.rb +8 -1
  44. metadata +17 -50
  45. data/lib/aggro/abstract_store.rb +0 -12
  46. data/lib/aggro/nanomsg_transport/connection.rb +0 -98
  47. data/lib/aggro/nanomsg_transport/publish.rb +0 -17
  48. data/lib/aggro/nanomsg_transport/raw_reply.rb +0 -18
  49. data/lib/aggro/nanomsg_transport/raw_request.rb +0 -18
  50. data/lib/aggro/nanomsg_transport/reply.rb +0 -17
  51. data/lib/aggro/nanomsg_transport/request.rb +0 -17
  52. data/lib/aggro/nanomsg_transport/server.rb +0 -84
  53. data/lib/aggro/nanomsg_transport/socket_error.rb +0 -20
  54. data/lib/aggro/nanomsg_transport/subscribe.rb +0 -27
  55. data/spec/lib/aggro/abstract_store_spec.rb +0 -15
  56. data/spec/lib/aggro/nanomsg_transport/socket_error_spec.rb +0 -21
@@ -1,12 +0,0 @@
1
- module Aggro
2
- # Private: Abstract class for an event store.
3
- class AbstractStore
4
- def read(_refs)
5
- fail NotImplementedError
6
- end
7
-
8
- def write(_event_streams)
9
- fail NotImplementedError
10
- end
11
- end
12
- end
@@ -1,98 +0,0 @@
1
- require 'ffi'
2
- require 'nn-core'
3
- require 'aggro/nanomsg_transport/socket_error'
4
-
5
- module Aggro
6
- module NanomsgTransport
7
- # Private: Base class for nanomsg socket wrappers.
8
- class Connection
9
- attr_reader :socket
10
-
11
- def initialize(endpoint)
12
- @endpoint = endpoint
13
- @rcv_buffer = FFI::MemoryPointer.new(:pointer)
14
-
15
- allocate_socket
16
- setup_socket
17
- set_endpoint
18
- end
19
-
20
- def setup_socket
21
- set_socket_option NNCore::NN_LINGER, 100
22
- set_socket_option NNCore::NN_SNDBUF, 131_072
23
- set_socket_option NNCore::NN_RCVBUF, 131_072
24
- end
25
-
26
- def send_msg(stringable)
27
- msg = stringable.to_s
28
- nbytes = NNCore::LibNanomsg.nn_send(@socket, msg, msg.bytesize, 0)
29
- assert(nbytes)
30
-
31
- nbytes
32
- end
33
-
34
- def recv_fd
35
- get_socket_option NNCore::NN_RCVFD
36
- end
37
-
38
- def recv_msg
39
- nbytes = NNCore::LibNanomsg.nn_recv(@socket, @rcv_buffer,
40
- NNCore::NN_MSG, 0)
41
-
42
- assert(nbytes)
43
-
44
- str = @rcv_buffer.read_pointer
45
- response = str.read_string(nbytes)
46
- NNCore::LibNanomsg.nn_freemsg str
47
-
48
- response
49
- rescue SocketError => e
50
- raise e unless e.errno == NNCore::EAGAIN
51
-
52
- nil
53
- end
54
-
55
- def terminate
56
- assert NNCore::LibNanomsg.nn_close(@socket)
57
- end
58
-
59
- protected
60
-
61
- def assert(rc)
62
- fail SocketError.new NNCore::LibNanomsg.nn_errno unless rc >= 0
63
- end
64
-
65
- def get_socket_option(setting, level = NNCore::NN_SOL_SOCKET)
66
- result = FFI::MemoryPointer.new(:int32)
67
- size = FFI::MemoryPointer.new(:size_t)
68
- size.write_int result.size
69
-
70
- rc = NNCore::LibNanomsg.nn_getsockopt(@socket, level, setting,
71
- result, size)
72
-
73
- assert(rc)
74
-
75
- result.read_int
76
- end
77
-
78
- def prepare_socket_option(value)
79
- if value.is_a? String
80
- [value, value.bytesize]
81
- else
82
- option = FFI::MemoryPointer.new(:int32)
83
- option.write_int(value)
84
-
85
- [option, 4]
86
- end
87
- end
88
-
89
- def set_socket_option(setting, value, level = NNCore::NN_SOL_SOCKET)
90
- option, option_length = prepare_socket_option(value)
91
-
92
- rc = NNCore::LibNanomsg.nn_setsockopt(@socket, level, setting,
93
- option, option_length)
94
- assert(rc)
95
- end
96
- end
97
- end
98
- end
@@ -1,17 +0,0 @@
1
- require 'aggro/nanomsg_transport/connection'
2
-
3
- module Aggro
4
- module NanomsgTransport
5
- # Private: Wrapper for a nanomsg PUB node.
6
- class Publish < Connection
7
- def allocate_socket
8
- @socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP, NNCore::NN_PUB)
9
- assert @socket
10
- end
11
-
12
- def set_endpoint
13
- assert NNCore::LibNanomsg.nn_bind(@socket, @endpoint)
14
- end
15
- end
16
- end
17
- end
@@ -1,18 +0,0 @@
1
- require 'aggro/nanomsg_transport/connection'
2
-
3
- module Aggro
4
- module NanomsgTransport
5
- # Private: Wrapper for a nanomsg XREP node.
6
- class RawReply < Connection
7
- def allocate_socket
8
- @socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP_RAW,
9
- NNCore::NN_REP)
10
- assert @socket
11
- end
12
-
13
- def set_endpoint
14
- assert NNCore::LibNanomsg.nn_bind(@socket, @endpoint)
15
- end
16
- end
17
- end
18
- end
@@ -1,18 +0,0 @@
1
- require 'aggro/nanomsg_transport/connection'
2
-
3
- module Aggro
4
- module NanomsgTransport
5
- # Private: Wrapper for a nanomsg XREQ node.
6
- class RawRequest < Connection
7
- def allocate_socket
8
- @socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP_RAW,
9
- NNCore::NN_REQ)
10
- assert @socket
11
- end
12
-
13
- def set_endpoint
14
- assert NNCore::LibNanomsg.nn_bind(@socket, @endpoint)
15
- end
16
- end
17
- end
18
- end
@@ -1,17 +0,0 @@
1
- require 'aggro/nanomsg_transport/connection'
2
-
3
- module Aggro
4
- module NanomsgTransport
5
- # Private: Wrapper for a nanomsg REP node.
6
- class Reply < Connection
7
- def allocate_socket
8
- @socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP, NNCore::NN_REP)
9
- assert @socket
10
- end
11
-
12
- def set_endpoint
13
- assert NNCore::LibNanomsg.nn_connect(@socket, @endpoint)
14
- end
15
- end
16
- end
17
- end
@@ -1,17 +0,0 @@
1
- require 'aggro/nanomsg_transport/connection'
2
-
3
- module Aggro
4
- module NanomsgTransport
5
- # Private: Wrapper for a nanomsg REQ node.
6
- class Request < Connection
7
- def allocate_socket
8
- @socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP, NNCore::NN_REQ)
9
- assert @socket
10
- end
11
-
12
- def set_endpoint
13
- assert NNCore::LibNanomsg.nn_connect(@socket, @endpoint)
14
- end
15
- end
16
- end
17
- end
@@ -1,84 +0,0 @@
1
- require 'nio'
2
-
3
- require 'aggro/nanomsg_transport/raw_reply'
4
- require 'aggro/nanomsg_transport/raw_request'
5
- require 'aggro/nanomsg_transport/reply'
6
-
7
- module Aggro
8
- module NanomsgTransport
9
- # Public: Server to handle messages from nanomsg clients.
10
- class Server
11
- DEFAULT_WORKER_COUNT = 16
12
-
13
- class ServerAlreadyRunning < RuntimeError; end
14
-
15
- def initialize(endpoint, callable = nil, &block)
16
- fail ArgumentError unless callable || block_given?
17
-
18
- @callable = block_given? ? block : callable
19
- @endpoint = endpoint
20
- @selectors = DEFAULT_WORKER_COUNT.times.map { NIO::Selector.new }
21
- @inproc_endpoint = "inproc://aggro-server-#{SecureRandom.hex}"
22
-
23
- ObjectSpace.define_finalizer self, method(:stop)
24
- end
25
-
26
- def start
27
- fail ServerAlreadyRunning if @running
28
-
29
- @running = true
30
- start_master
31
- DEFAULT_WORKER_COUNT.times { |i| start_worker i }
32
-
33
- sleep 0.01 while @selectors.any?(&:empty?)
34
-
35
- self
36
- end
37
-
38
- def stop
39
- return self unless @running
40
-
41
- @running = false
42
- @selectors.each(&:wakeup)
43
-
44
- sleep 0.01 until @selectors.any?(&:empty?)
45
-
46
- @raw_reply.terminate
47
- @raw_request.terminate
48
-
49
- self
50
- end
51
-
52
- private
53
-
54
- def handle_request(socket)
55
- message = socket.recv_msg
56
- socket.send_msg @callable.call(message) if @running
57
- end
58
-
59
- def start_master
60
- @master_thread = Concurrent::SingleThreadExecutor.new.post do
61
- @raw_reply = RawReply.new(@endpoint)
62
- @raw_request = RawRequest.new(@inproc_endpoint)
63
-
64
- NNCore::LibNanomsg.nn_device @raw_request.socket, @raw_reply.socket
65
- end
66
- end
67
-
68
- def start_worker(i)
69
- Concurrent::SingleThreadExecutor.new.post do
70
- reply = Reply.new(@inproc_endpoint)
71
- io = IO.new(reply.recv_fd, 'rb', autoclose: false)
72
-
73
- @selectors[i].register io, :r
74
-
75
- @selectors[i].select { handle_request(reply) } while @running
76
-
77
- @selectors[i].deregister io
78
- io.close
79
- reply.terminate
80
- end
81
- end
82
- end
83
- end
84
- end
@@ -1,20 +0,0 @@
1
- module Aggro
2
- module NanomsgTransport
3
- # Public: An error calling the nanomsg API.
4
- class SocketError < RuntimeError
5
- attr_reader :errno
6
-
7
- def initialize(errno)
8
- @errno = errno
9
- end
10
-
11
- def error_message
12
- NNCore::LibNanomsg.nn_strerror(errno)
13
- end
14
-
15
- def to_s
16
- "Last nanomsg API call failed with '#{error_message}'"
17
- end
18
- end
19
- end
20
- end
@@ -1,27 +0,0 @@
1
- require 'aggro/nanomsg_transport/connection'
2
-
3
- module Aggro
4
- module NanomsgTransport
5
- # Private: Wrapper for a nanomsg SUB node.
6
- class Subscribe < Connection
7
- def add_subscription(topic)
8
- set_socket_option NNCore::NN_SUB_SUBSCRIBE, topic, NNCore::NN_SUB
9
- end
10
-
11
- def allocate_socket
12
- @socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP, NNCore::NN_SUB)
13
- assert @socket
14
- end
15
-
16
- def set_endpoint
17
- assert NNCore::LibNanomsg.nn_connect(@socket, @endpoint)
18
- end
19
-
20
- def setup_socket
21
- super
22
-
23
- set_socket_option NNCore::NN_RCVTIMEO, 100
24
- end
25
- end
26
- end
27
- end
@@ -1,15 +0,0 @@
1
- RSpec.describe AbstractStore do
2
- subject(:store) { AbstractStore.new }
3
-
4
- describe '#read' do
5
- it 'should raise not implemented error' do
6
- expect { store.read(double) }.to raise_error NotImplementedError
7
- end
8
- end
9
-
10
- describe '#write' do
11
- it 'should raise not implemented error' do
12
- expect { store.write(double) }.to raise_error NotImplementedError
13
- end
14
- end
15
- end
@@ -1,21 +0,0 @@
1
- RSpec.describe NanomsgTransport::SocketError do
2
- subject(:error) { NanomsgTransport::SocketError.new 5 }
3
-
4
- let(:nanomsg_api) { spy nn_strerror: 'No u' }
5
-
6
- before do
7
- stub_const 'NNCore::LibNanomsg', nanomsg_api
8
- end
9
-
10
- describe '#to_s' do
11
- it 'should ask nanomsg for the meaning of the errno' do
12
- error.to_s
13
-
14
- expect(nanomsg_api).to have_received(:nn_strerror).with 5
15
- end
16
-
17
- it 'should provide a human readable error message' do
18
- expect(error.to_s).to eq "Last nanomsg API call failed with 'No u'"
19
- end
20
- end
21
- end