peplum 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6624c27866af54f4e3ab25e39a1fedb7ce83039b56c3bf6f43e5be42b19b0025
4
+ data.tar.gz: 8895326c25f1745c2074495e4a7666b2055a6e9efd765f90f32135f856c4e4d6
5
+ SHA512:
6
+ metadata.gz: aa10f79c3ce4553d8237da0b0efec0578b2419e3794cefb75f036173acb5c6dfb76f34e743c150c60f919351699e4978944faa01535ed0b89bc7864232abf14a
7
+ data.tar.gz: bc66f042bb08248ec3c13ba01b4aea90c80e56ec50016b74e7f38e77b965a40c4745983ed470699ac897ce89df742295a0cbe675006e247719913c8fc18086ed
data/bin/.gitkeep ADDED
File without changes
@@ -0,0 +1,32 @@
1
+ module Peplum
2
+ class Application
3
+
4
+ class Peers
5
+
6
+ def initialize
7
+ @peers = {}
8
+ end
9
+
10
+ def set( peer_info )
11
+ peer_info.each do |url, token|
12
+ next if url == self.self_url
13
+ @peers[url] = Peplum::Application.connect( url: url, token: token )
14
+ end
15
+
16
+ nil
17
+ end
18
+
19
+ def each( &block )
20
+ @peers.each do |_, client|
21
+ block.call client
22
+ end
23
+ end
24
+
25
+ def self_url
26
+ Cuboid::Options.rpc.url
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,62 @@
1
+ module Peplum
2
+ class Application
3
+ module Services
4
+
5
+ class Scheduler
6
+
7
+ # Keep those out of RPC.
8
+ class <<self
9
+ def get_worker
10
+ worker_info = agent.spawn
11
+ return if !worker_info
12
+
13
+ worker = Peplum::Application.connect( worker_info )
14
+ self.workers[worker.url] = worker
15
+ worker
16
+ end
17
+
18
+ def done_signal
19
+ @done_signal ||= Queue.new
20
+ end
21
+
22
+ def wait
23
+ self.done_signal.pop
24
+ end
25
+
26
+ def done
27
+ self.done_signal << nil
28
+ end
29
+
30
+ def agent
31
+ @agent ||= Processes::Agents.connect( Cuboid::Options.agent.url )
32
+ end
33
+
34
+ def workers
35
+ @workers ||= {}
36
+ end
37
+ end
38
+
39
+ def report( data, url )
40
+ return if !(worker = self.class.workers.delete( url ))
41
+
42
+ report_data << data
43
+
44
+ worker.shutdown {}
45
+ return unless self.class.workers.empty?
46
+
47
+ Cuboid::Application.application.report report_data
48
+
49
+ self.class.done
50
+ end
51
+
52
+ private
53
+
54
+ def report_data
55
+ @report_data ||= []
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,65 @@
1
+ module Peplum
2
+ class Application
3
+ module Services
4
+
5
+ class SharedHash
6
+
7
+ def initialize
8
+ @hash = {}
9
+ end
10
+
11
+ def get( k )
12
+ @hash[k]
13
+ end
14
+
15
+ def set( k, v, broadcast = true, &block )
16
+ if @hash[k] == v
17
+ block.call if block_given?
18
+ return
19
+ end
20
+
21
+ @hash[k] = v
22
+
23
+ if broadcast
24
+ each_peer do |peer|
25
+ peer.shared_hash.set( k, v, false ) {}
26
+ end
27
+ end
28
+
29
+ block.call if block_given?
30
+ nil
31
+ end
32
+
33
+ def delete( k, broadcast = true )
34
+ if !@hash.include? k
35
+ block.call if block_given?
36
+ return
37
+ end
38
+
39
+ @hash.delete( k )
40
+
41
+ if broadcast
42
+ each_peer do |_, peer|
43
+ peer.shared_hash.delete( k, false ) {}
44
+ end
45
+ end
46
+
47
+ block.call if block_given?
48
+ nil
49
+ end
50
+
51
+ def to_h
52
+ @hash.dup
53
+ end
54
+
55
+ private
56
+
57
+ def each_peer( &block )
58
+ Cuboid::Application.application.peers.each( &block )
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,155 @@
1
+ require 'cuboid'
2
+ require 'json'
3
+ require 'peplum'
4
+ require 'peplum/core_ext/array'
5
+
6
+ module Peplum
7
+ class Application < Cuboid::Application
8
+ require 'peplum/application/peers'
9
+
10
+ require 'peplum/application/services/shared_hash'
11
+ require 'peplum/application/services/scheduler'
12
+
13
+ class Error < Peplum::Error; end
14
+
15
+ class <<self
16
+ def inherited( application )
17
+ super
18
+
19
+ Cuboid::Application.application = application
20
+
21
+ application.validate_options_with :validate_options
22
+ application.serialize_with JSON
23
+
24
+ application.instance_service_for :scheduler, Services::Scheduler
25
+ application.instance_service_for :shared_hash, Services::SharedHash
26
+ end
27
+ end
28
+
29
+ attr_reader :peers
30
+
31
+ def initialize(*)
32
+ super
33
+
34
+ @peers = Peers.new
35
+ end
36
+
37
+ def run
38
+ Raktr.global.on_error do |_, e|
39
+ $stderr.puts e
40
+ end
41
+
42
+ options = @options.dup
43
+ peplum_options = options.delete( 'peplum' )
44
+ native_options = options.delete( 'native' )
45
+
46
+ # We have a master so we're not the scheduler, run the payload.
47
+ if peplum_options['master']
48
+ execute( peplum_options, native_options )
49
+
50
+ # We're the scheduler Instance, get to grouping and spawning.
51
+ else
52
+ schedule( peplum_options, native_options )
53
+ end
54
+ end
55
+
56
+ # Implements:
57
+ # * `.run` -- Worker; executes its payload against `objects`.
58
+ # * `.group` -- Splits given `objects` into groups for each worker.
59
+ # * `.merge` -- Merges results from multiple workers.
60
+ #
61
+ # That's all we need to turn any application into a super version of itself.
62
+ #
63
+ # @abstract
64
+ def native_app
65
+ fail Error, 'Missing native app!'
66
+ end
67
+
68
+ def report( data )
69
+ super native_app.merge( data )
70
+ end
71
+
72
+ private
73
+
74
+ def execute( peplum_options, native_options )
75
+ master_info = peplum_options.delete( 'master' )
76
+
77
+ self.peers.set( peplum_options.delete( 'peers' ) || {} )
78
+
79
+ report_data = native_app.run( peplum_options['objects'], native_options )
80
+
81
+ master = Processes::Instances.connect( master_info['url'], master_info['token'] )
82
+ master.scheduler.report report_data, Cuboid::Options.rpc.url
83
+ end
84
+
85
+ def schedule( peplum_options, native_options )
86
+ max_workers = peplum_options.delete('max_workers')
87
+ objects = peplum_options.delete('objects')
88
+ groups = native_app.group( objects, max_workers )
89
+
90
+ # Workload turned out to be less than our maximum allowed instances.
91
+ # Don't spawn the max if we don't have to.
92
+ if groups.size < max_workers
93
+ instance_num = groups.size
94
+
95
+ # Workload distribution turned out as expected.
96
+ elsif groups.size == max_workers
97
+ instance_num = max_workers
98
+
99
+ # What the hell did just happen1?
100
+ else
101
+ fail Error, 'Workload distribution error, uneven grouping!'
102
+ end
103
+
104
+ scheduler = self.scheduler.class
105
+ instance_num.times.each do |i|
106
+ # Get as many workers as necessary/possible.
107
+ break unless scheduler.get_worker
108
+ end
109
+
110
+ # We couldn't get the workers we were going for, Grid reached its capacity,
111
+ # re-balance distribution.
112
+ if scheduler.workers.size < groups.size
113
+ groups = native_app.group( objects, scheduler.workers.size )
114
+ end
115
+
116
+ peers = Hash[scheduler.workers.values.map { |client| [client.url, client.token] }]
117
+
118
+ scheduler.workers.values.each do |worker|
119
+ worker.run(
120
+ peplum: {
121
+ objects: groups.pop,
122
+ peers: peers,
123
+ master: {
124
+ url: Cuboid::Options.rpc.url,
125
+ token: Cuboid::Options.datastore.token
126
+ }
127
+ },
128
+ native: native_options
129
+ )
130
+ end
131
+
132
+ scheduler.wait
133
+ end
134
+
135
+ def validate_options( options )
136
+ if !Cuboid::Options.agent.url
137
+ fail Error, 'Missing Agent!'
138
+ end
139
+
140
+ peplum_options = options['peplum']
141
+
142
+ if !peplum_options.include? 'objects'
143
+ fail Error, 'Options: Missing :objects'
144
+ end
145
+
146
+ if !peplum_options['master'] && !peplum_options.include?( 'max_workers' )
147
+ fail Error, 'Options: Missing :max_workers'
148
+ end
149
+
150
+ @options = options
151
+ true
152
+ end
153
+
154
+ end
155
+ end
@@ -0,0 +1,20 @@
1
+ class Array
2
+
3
+ def chunk( pieces = 2 )
4
+ return self if pieces <= 0
5
+
6
+ len = self.length
7
+ mid = len / pieces
8
+ chunks = []
9
+ start = 0
10
+
11
+ 1.upto( pieces ) do |i|
12
+ last = start + mid
13
+ last = last - 1 unless len % pieces >= i
14
+ chunks << self[ start..last ] || []
15
+ start = last + 1
16
+ end
17
+
18
+ chunks
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peplum
4
+ VERSION = "0.1.0"
5
+ end
data/lib/peplum.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "peplum/version"
4
+
5
+ module Peplum
6
+ class Error < StandardError; end
7
+ # Your code goes here...
8
+
9
+ require "peplum/application"
10
+ end
data/peplum.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/peplum/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "peplum"
7
+ spec.version = Peplum::VERSION
8
+ spec.authors = ["Tasos Laskos"]
9
+ spec.email = ["tasos.laskos@gmail.com"]
10
+
11
+ spec.summary = "Distributed computing made easy."
12
+ spec.description = "Distributed computing made easy."
13
+ spec.homepage = "http://ecsypno.com/"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.files = Dir.glob( 'bin/*')
17
+ spec.files += %w(bin/.gitkeep)
18
+ spec.files += Dir.glob( 'lib/**/*')
19
+ spec.files += Dir.glob( 'examples/**/*')
20
+ spec.files += %w(peplum.gemspec)
21
+
22
+ spec.add_dependency 'cuboid'
23
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: peplum
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tasos Laskos
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-05-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cuboid
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Distributed computing made easy.
28
+ email:
29
+ - tasos.laskos@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - bin/.gitkeep
35
+ - lib/peplum.rb
36
+ - lib/peplum/application.rb
37
+ - lib/peplum/application/peers.rb
38
+ - lib/peplum/application/services/scheduler.rb
39
+ - lib/peplum/application/services/shared_hash.rb
40
+ - lib/peplum/core_ext/array.rb
41
+ - lib/peplum/version.rb
42
+ - peplum.gemspec
43
+ homepage: http://ecsypno.com/
44
+ licenses: []
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.6.0
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubygems_version: 3.4.13
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: Distributed computing made easy.
65
+ test_files: []