peplum 0.1.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.
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: []