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 +7 -0
- data/bin/.gitkeep +0 -0
- data/lib/peplum/application/peers.rb +32 -0
- data/lib/peplum/application/services/scheduler.rb +62 -0
- data/lib/peplum/application/services/shared_hash.rb +65 -0
- data/lib/peplum/application.rb +155 -0
- data/lib/peplum/core_ext/array.rb +20 -0
- data/lib/peplum/version.rb +5 -0
- data/lib/peplum.rb +10 -0
- data/peplum.gemspec +23 -0
- metadata +65 -0
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
|
data/lib/peplum.rb
ADDED
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: []
|