clustered_rpc 0.1.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9bc292ea42805f08ade35dbd5ccc1856fecfdebff8443c943ee9bc4f9dd4a72
4
- data.tar.gz: 3d18c96eecd6b8fe288a9130a9f15b660cc4031495d196c0fed5ea1a7aad1fb6
3
+ metadata.gz: 904505b16c185f8541a99935ce6f1e5238069f3f3d69bc95c440d7c7a6669357
4
+ data.tar.gz: 6557b5bf8f8a58120ab25f70301bf53ef8f7959d57f478b1b0589327a0d1302f
5
5
  SHA512:
6
- metadata.gz: 54f0b6a5c19a38ef87c0acd1c75449a307fc4605812baa6986fb5ae0e4f1ac6abce3211e5b4989329212b885f7de9f9d99b89f115287ab4c6f5b89bbfe2d628d
7
- data.tar.gz: 1e9fe2a6c30c8c8e970ee648b6fd57e8f9a31900507a820b5bddccccd140d68c905798fc0d40c1ae7da61f637b2c01dbc35886cd865187e2f59e65759376cbd3
6
+ metadata.gz: 0155fd98b49b9351d9cf4a3086fc9145e1095bc41ff8a2638a800a61ca73a19caefbf43c9a0b9ef458a1e847928077b7b01f5b9da27bf591395f697930fcab8b
7
+ data.tar.gz: 9facf71c00b00091591e2261e1ccb46e302ee0fa8f3e1d3f9b362305a48c53986a33ba6a797baf07881548261b358d81a95ac050992e49d2e027170aee7f47f7
@@ -0,0 +1,51 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ main ]
13
+ pull_request:
14
+ branches: [ main ]
15
+
16
+ jobs:
17
+ test:
18
+
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ matrix:
22
+ ruby-version: [ '2.5', '2.6', '2.7']
23
+
24
+ # Service containers to run with `runner-job`
25
+ services:
26
+ # Label used to access the service container
27
+ redis:
28
+ # Docker Hub image
29
+ image: redis
30
+ # Set health checks to wait until redis has started
31
+ options: >-
32
+ --health-cmd "redis-cli ping"
33
+ --health-interval 10s
34
+ --health-timeout 5s
35
+ --health-retries 5
36
+ ports:
37
+ # Maps port 6379 on service container to the host
38
+ - 6379:6379
39
+
40
+ steps:
41
+ - uses: actions/checkout@v2
42
+ - name: Set up Ruby
43
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
44
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
45
+ # uses: ruby/setup-ruby@v1
46
+ uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
47
+ with:
48
+ ruby-version: ${{ matrix.ruby-version }}
49
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
50
+ - name: Run tests
51
+ run: bundle exec rake
data/.travis.yml CHANGED
@@ -3,5 +3,14 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.5.1
6
+ - 2.5
7
+ - 2.6
8
+ - 2.7
9
+ services:
10
+ - redis-server
11
+
7
12
  before_install: gem install bundler -v 2.0.2
13
+
14
+ #addons:
15
+ # code_climate:
16
+ # repo_token:
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- clustered_rpc (0.1.0)
4
+ clustered_rpc (0.3.1)
5
5
  activesupport
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activesupport (6.1.3.1)
10
+ activesupport (6.1.3.2)
11
11
  concurrent-ruby (~> 1.0, >= 1.0.2)
12
12
  i18n (>= 1.6, < 2)
13
13
  minitest (>= 5.1)
@@ -16,14 +16,26 @@ GEM
16
16
  awesome_print (1.8.0)
17
17
  byebug (11.1.3)
18
18
  concurrent-ruby (1.1.8)
19
+ coveralls (0.7.2)
20
+ multi_json (~> 1.3)
21
+ rest-client (= 1.6.7)
22
+ simplecov (>= 0.7)
23
+ term-ansicolor (= 1.2.2)
24
+ thor (= 0.18.1)
19
25
  diff-lcs (1.4.4)
20
26
  docile (1.3.2)
21
27
  i18n (1.8.10)
22
28
  concurrent-ruby (~> 1.0)
23
29
  json (2.3.0)
30
+ mime-types (3.3.1)
31
+ mime-types-data (~> 3.2015)
32
+ mime-types-data (3.2021.0225)
24
33
  minitest (5.14.4)
34
+ multi_json (1.15.0)
25
35
  rake (10.5.0)
26
36
  redis (4.2.2)
37
+ rest-client (1.6.7)
38
+ mime-types (>= 1.16)
27
39
  rspec (3.10.0)
28
40
  rspec-core (~> 3.10.0)
29
41
  rspec-expectations (~> 3.10.0)
@@ -42,6 +54,10 @@ GEM
42
54
  json (>= 1.8, < 3)
43
55
  simplecov-html (~> 0.10.0)
44
56
  simplecov-html (0.10.2)
57
+ term-ansicolor (1.2.2)
58
+ tins (~> 0.8)
59
+ thor (0.18.1)
60
+ tins (0.13.2)
45
61
  tzinfo (2.0.4)
46
62
  concurrent-ruby (~> 1.0)
47
63
  zeitwerk (2.4.2)
@@ -54,6 +70,7 @@ DEPENDENCIES
54
70
  bundler (~> 2.0)
55
71
  byebug
56
72
  clustered_rpc!
73
+ coveralls (>= 0.7.0)
57
74
  rake (~> 10.0)
58
75
  redis
59
76
  rspec (~> 3.0)
data/README.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # ClusteredRpc
2
+ [![RubyGems][gem_version_badge]][ruby_gems]
3
+ [![Travis CI][travis_ci_badge]][travis_ci]
4
+ [![Coveralls][coveralls_badge]][coveralls]
2
5
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/clustered`. To experiment with that code, run `bin/console` for an interactive prompt.
6
+ _RPC = Remote Procedure Calls_
7
+
8
+ ClusteredRpc allows you to run code on every ruby process running within your cluster.
9
+
10
+ Clusters are defined using a shared pubsub broker and a common namespace.
11
+
12
+ Currently only Redis PubSub is supported by ClusteredRpc.
4
13
 
5
- TODO: Delete this and the text above, and describe your gem
6
14
 
7
15
  ## Installation
8
16
 
@@ -20,24 +28,110 @@ Or install it yourself as:
20
28
 
21
29
  $ gem install clustered_rpc
22
30
 
31
+ ## Configuration
32
+ ```ruby
33
+ # Using Redis PubSub
34
+ ClusteredRpc.config do |c|
35
+ c.transport_class = ClusteredRpc::Transport::RedisCluster
36
+ c.cluster_namespace = "myapplication"
37
+ c.options = {redis_url: "redis://127.0.0.1:6379/3"}
38
+ end
39
+
40
+ ```
41
+ If you are using the same redis server for multiple deployments of your application, then use different namespaces for each.
42
+ ```ruby
43
+ ClusteredRpc.config do |c|
44
+ c.transport_class = ClusteredRpc::Transport::RedisCluster
45
+ # Using the same redis database for the development environment as well...
46
+ c.cluster_namespace = "myapplication_dev"
47
+ c.options = {redis_url: "redis://127.0.0.1:6379/3"}
48
+ end
49
+ ```
23
50
  ## Usage
24
51
 
25
- Works on *static* class methods!!!
52
+ ClusteredRpc allows static (class) methods to be run on every process within the cluster!
26
53
 
27
54
  ```ruby
28
55
  class MyClass
56
+ # makes all static-methods available via 'clustered_rpc' proxy method
29
57
  include ClusteredRpc::Methods
30
58
 
31
59
  def self.do_the_thing
32
60
  # important code living on many servers in the cluster
33
- puts "I'm important!"
61
+ return "I'm important!"
34
62
  end
35
63
  end
36
64
 
37
65
  # Run the method on every process running in the cluster
66
+ # `do_the_thing` is run on each process and the results are returned in a Hash
38
67
  MyClass.clustered_rpc.do_the_thing
68
+ => {
69
+ :request_id => "f030b020e058d7a4",
70
+ :success => true,
71
+ :results => {
72
+ "1ca8a7be5e" => {
73
+ "seconds" => 3.3e-05,
74
+ "result" => "I'm important"
75
+ },
76
+ "293a7be9ac" => {
77
+ "seconds" => 4.2e-05,
78
+ "result" => "I'm important"
79
+ }
80
+ }
81
+ }
39
82
  ```
83
+ The keys in the results Hash (`1ca8a7be5e` and `293a7be9ac`) are the unique instance_ids assigned to each process by ClusteredRpc
84
+
85
+ ```ruby
86
+ # Get the stats for your entire cluster
87
+ ClusteredRpc::Info.clustered_rpc.stats
88
+ => {
89
+ :request_id => "c634e6347b98a0",
90
+ :success => true,
91
+ :results => {
92
+ "ica817be5e" => {
93
+ :instance_id => "1ca8a7be5e",
94
+ :transport_class => "ClusteredRpc::Transport::LocalProcess",
95
+ :options => {},
96
+ :process_id => 23566,
97
+ :uptime => "03:14",
98
+ :used_mb => 35.03,
99
+ :startup_command => "ruby-2.5.1/bin/rails console",
100
+ :process_type => "Rails Console",
101
+ :count_nodes => {}
102
+ },
103
+ {
104
+ :instance_id => "293a7be9ac",
105
+ :transport_class => "ClusteredRpc::Transport::LocalProcess",
106
+ :options => {},
107
+ :process_id => 23736,
108
+ :uptime => "42:22",
109
+ :used_mb => 127.69,
110
+ :startup_command => "ruby-2.5.1/bin/puma",
111
+ :process_type => "Web Server",
112
+ :count_nodes => {}
113
+ }
114
+
115
+ }
116
+ }
40
117
 
118
+ ```
119
+ Of course, methods can be run locally (without `.clustered_rpc`) as well
120
+ ```ruby
121
+ # Get the stats for the local process
122
+ ClusteredRpc::Info.stats
123
+ => {
124
+ :instance_id => "1ca8a7be5e",
125
+ :transport_class => "ClusteredRpc::Transport::LocalProcess",
126
+ :options => {},
127
+ :process_id => 23566,
128
+ :uptime => "00:01",
129
+ :used_mb => 35.03,
130
+ :startup_command => "ruby-2.5.1/bin/rails console",
131
+ :process_type => "Rails Console",
132
+ :count_nodes => {}
133
+ }
134
+ ```
41
135
  ## Development
42
136
 
43
137
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -46,8 +140,15 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
46
140
 
47
141
  ## Contributing
48
142
 
49
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/clustered.
143
+ Bug reports and pull requests are welcome on GitHub at https://github.com/megalithtracers/clustered_rpc.
50
144
 
51
145
  ## License
52
146
 
53
147
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
148
+
149
+ [gem_version_badge]: https://img.shields.io/gem/v/clustered_rpc.svg?style=flat
150
+ [ruby_gems]: http://rubygems.org/gems/clustered_rpc
151
+ [travis_ci]: http://travis-ci.org/megalithtracers/clustered_rpc
152
+ [travis_ci_badge]: https://travis-ci.com/megalithtracers/clustered_rpc.svg?branch=main
153
+ [coveralls_badge]: https://img.shields.io/coveralls/github/megalithtracers/clustered_rpc/main
154
+ [coveralls]: https://coveralls.io/github/megalithtracers/clustered_rpc
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency "awesome_print"
34
34
  spec.add_development_dependency "bundler", "~> 2.0"
35
35
  spec.add_development_dependency "byebug"
36
+ spec.add_development_dependency "coveralls", ">= 0.7.0"
36
37
  spec.add_development_dependency "rake", "~> 10.0"
37
38
  spec.add_development_dependency "redis"
38
39
  spec.add_development_dependency "rspec", "~> 3.0"
data/lib/clustered_rpc.rb CHANGED
@@ -5,12 +5,14 @@ require "json"
5
5
  require "active_support/concern"
6
6
  require "clustered_rpc/proxy"
7
7
  require "clustered_rpc/transport/base"
8
+ require "clustered_rpc/transport/local_process"
8
9
 
9
10
  module ClusteredRpc
10
11
  class Error < StandardError; end
11
12
 
12
13
  @@instance_id = SecureRandom.hex(5)
13
14
  @@logger = ::Logger.new(STDOUT)
15
+ @@cluster_namespace = "clustered_rpc"
14
16
  @@transport_class = nil
15
17
  @@transport = nil
16
18
  @@options = {}
@@ -21,6 +23,9 @@ module ClusteredRpc
21
23
  def self.instance_id=(instance_id); @@instance_id = instance_id; end
22
24
  def self.instance_id; @@instance_id; end
23
25
 
26
+ def self.cluster_namespace=(cluster_namespace); @@cluster_namespace = cluster_namespace; end
27
+ def self.cluster_namespace; @@cluster_namespace; end
28
+
24
29
  def self.transport_class=(transport_class); @@transport_class = transport_class; end
25
30
  def self.transport_class; @@transport_class; end
26
31
 
@@ -29,7 +34,8 @@ module ClusteredRpc
29
34
 
30
35
  # request_id should have been returned from the call to #cluster_send
31
36
  def self.get_result(request_id, wait_seconds = 1.0)
32
- sleep(wait_seconds) if wait_seconds
37
+ # Don't wait at all when using LocalProcess
38
+ sleep(wait_seconds) if wait_seconds && transport_class != ClusteredRpc::Transport::LocalProcess
33
39
  results = @@transport.get_result(request_id)
34
40
  results.keys.each{|k| results[k] = JSON.parse(results[k])}
35
41
  results # ??? Rails anyone? .with_indifferent_access
@@ -38,14 +44,11 @@ module ClusteredRpc
38
44
 
39
45
 
40
46
  def self.config(force=false, &block)
47
+ @@transport = nil
41
48
  block.call(self)
42
-
43
49
  @@instance_id ||= SecureRandom.hex(5)
44
- raise "Please set transport_class in ClusteredRpc.config" if transport_class.nil?
45
- logger.info "Clustered using #{@@transport_class}"
46
- @@transport = @@transport_class.new
50
+ ensure_transport
47
51
  @@transport.connect
48
-
49
52
  end
50
53
 
51
54
  def self.reconnect
@@ -57,9 +60,20 @@ module ClusteredRpc
57
60
  def self.publish(payload={})
58
61
  # if :request_id is already present, then we're responding with a process-level response
59
62
  # otherwise we're creating a new clustered_request and should generate a :request_io
63
+ ensure_transport
60
64
  payload[:request_id] ||= SecureRandom.hex(8)
61
65
  @@transport.publish payload
62
66
  payload[:request_id]
63
67
  end
64
68
 
69
+ private
70
+ def self.ensure_transport
71
+ return if @@transport
72
+ if @@transport_class.nil?
73
+ require "clustered_rpc/transport/local_process"
74
+ @@transport_class = ClusteredRpc::Transport::LocalProcess
75
+ end
76
+ logger.info "Clustered using #{@@transport_class}[#{@@cluster_namespace}]"
77
+ @@transport = @@transport_class.new
78
+ end
65
79
  end
@@ -1,12 +1,13 @@
1
- require 'clustered/rpc_methods'
1
+ require 'clustered_rpc/methods'
2
2
  require 'open3'
3
3
 
4
4
  module ClusteredRpc
5
5
  class Info
6
6
  include ClusteredRpc::Methods
7
7
 
8
- def self.stats
8
+ def self.stats(detailed_memory_stats = false)
9
9
  require 'objspace'
10
+ r =
10
11
  { instance_id: ClusteredRpc.instance_id,
11
12
  transport_class: ClusteredRpc.transport_class.name.to_s,
12
13
  options: ClusteredRpc.options,
@@ -15,11 +16,13 @@ module ClusteredRpc
15
16
  used_mb: memory_used,
16
17
  startup_command: startup_command,
17
18
  process_type: lookup_process_type(startup_command),
18
- count_objects_size: ObjectSpace.count_objects_size,
19
- gc: GC.stat,
20
- count_nodes: ObjectSpace.count_nodes
19
+ count_nodes: ObjectSpace.count_nodes,
21
20
  }
22
-
21
+ if detailed_memory_stats
22
+ r[:count_objects_size] = ObjectSpace.count_objects_size
23
+ r[:gc] = GC.stat
24
+ end
25
+ r
23
26
  end
24
27
 
25
28
  def self.startup_command
@@ -10,7 +10,7 @@ module ClusteredRpc
10
10
  request_id = ::ClusteredRpc.publish({'klass' => @target.name, 'method' => method, 'args' => args, 'kwargs' => kwargs}.merge(@options))
11
11
  {request_id: request_id, success: true, results: ::ClusteredRpc.get_result(request_id, wait_seconds)}
12
12
  rescue => e
13
- ClusteredRpc.logger.error "ClusteredRpc::Proxy encountered errror: #{e.message}"
13
+ ClusteredRpc.logger.error "ClusteredRpc::Proxy encountered error: #{e.message}"
14
14
  {request_id: "Error", success: false, results: e.message}
15
15
  end
16
16
 
@@ -7,14 +7,14 @@ module ClusteredRpc
7
7
  def initialize
8
8
  @redis_subscriber = nil
9
9
  @redis_publish = nil
10
- @redis_message_pubsub_key = "__cluster_messages"
10
+ @redis_message_pubsub_key = "__#{ClusteredRpc.cluster_namespace}_messages"
11
11
  connect
12
12
  end
13
13
 
14
14
  def publish(payload={})
15
15
  @redis_publish.publish @redis_message_pubsub_key, payload.to_json
16
16
  rescue => e
17
- ClusteredRpc.logger.error "ClusteredRpc.publish encountered errror: #{e.message}"
17
+ ClusteredRpc.logger.error "ClusteredRpc.publish encountered error: #{e.message}"
18
18
  raise e
19
19
  end
20
20
 
@@ -27,6 +27,8 @@ module ClusteredRpc
27
27
  if @subscriber_thread
28
28
  ClusteredRpc.logger.warn "ClusteredRpc: killing subscriber thread"
29
29
  @subscriber_thread.kill
30
+ # https://stackoverflow.com/questions/49490278/wait-for-a-thread-to-die-in-ruby
31
+ sleep 0.01 while @subscriber_thread.alive?
30
32
  @redis_subscriber = nil
31
33
  end
32
34
  connect
@@ -1,3 +1,3 @@
1
1
  module ClusteredRpc
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clustered_rpc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - megalithtracers@gmail.com
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-29 00:00:00.000000000 Z
11
+ date: 2021-07-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: coveralls
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 0.7.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.7.0
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -130,6 +144,7 @@ executables: []
130
144
  extensions: []
131
145
  extra_rdoc_files: []
132
146
  files:
147
+ - ".github/workflows/ruby.yml"
133
148
  - ".rspec"
134
149
  - ".travis.yml"
135
150
  - Gemfile