oni 0.0.1 → 1.0.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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NjhlYmExNmMzYzg4ZmY0YWQwMjcwNzE4YTA2OGYyYTQwOTg2ZmI3Nw==
4
+ Yzg0ZTUxZTdiN2E0YmVkM2M2NGFkYTNiMWY0ZDZiNDVhZDAyMmM1Ng==
5
5
  data.tar.gz: !binary |-
6
- NGQ3ODEzZmNhYmQ1NTVlMTQzY2IwN2ZjYTZhYWExOWRmZWMxNjZhOQ==
6
+ MzJiNjliMjhiZjcwZTk4NTkyZjFkZjU4YTI1YjJkMmEyNjQ0Y2EwYw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YTM4YWZiNzA4NzJlOWU4MGRhZjVkZjUxZjhkZmNmMDU5NDM4MGZmNTg0YmJh
10
- ZDg5NWE2NTI3MzNjMjQ2NDcwY2UyYzBjOGMzOGY1ZDg5YWM3ZDMzYmVjMzIz
11
- NTJlNjdjMDdjNGM0NmNkZDdhZWVhM2YzZjRhMjI0MGU3ZmZiZWQ=
9
+ OTE4MzgzNjMxZDI5YzZhYjVmYWQ2OTUzZWE1MTI5NzY5YzUzNWY0ZjY4MjNl
10
+ MzhhN2JkOGFjNTEzMzk2ODU0YjlhNTE5YmY2NGQ3MzMxZTI5MDk3YmFjZWU5
11
+ YjYwZmI5MmU0NGFmNjkwY2RhMGIxMzk1NDUwYzg4OTUwZGFiNTQ=
12
12
  data.tar.gz: !binary |-
13
- YzQwNzAzMDRlMDgyNGQ4Mjg2NWY0NmU3YjNhOTQxODYwNWI0NGU1M2YxZjA0
14
- ZTJkYTU0MmU4MTA3YmNkMTcxZWY5MmMzZDFhNmYyMDhhYmFkMDcwMWQyYjQ4
15
- Y2EzMjQyMTFiNmEyZGMwNmZmNTFmNWRhMDQ0YTc3MTQ1M2U2NjU=
13
+ NjAzNWExYjhmNWI0ZWQzMjY2NWY0NTMwYjQ1MjZlNjExNDg2NjJlNWY5MmNl
14
+ ZGY2Y2JiMTExMDgwNGE3ODVlMDJiNjk5NWRmMWM1ZjYwODg4YmQzNWMxYTUz
15
+ ZDFiNjcxZGM0OTdiYTBhYWZmMjE3MjcxOWVlZmY2MDZmMmMzODc=
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ coverage
2
+ yardoc
3
+ pkg
4
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,23 @@
1
+ ---
2
+ script: rake
3
+
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.0-preview1
8
+ - jruby
9
+ - jruby-head
10
+ - jruby-1.7.4
11
+ - rbx-2.1.1
12
+
13
+ notifications:
14
+ email:
15
+ recipients:
16
+ - development@olery.com
17
+ email:
18
+ on_success: change
19
+ on_failure: change
20
+
21
+ branches:
22
+ only:
23
+ - master
data/.yardopts ADDED
@@ -0,0 +1,12 @@
1
+ ./lib/oni/**/*.rb ./lib/oni.rb
2
+ -m markdown
3
+ -M kramdown
4
+ -o yardoc
5
+ -r ./README.md
6
+ --private
7
+ --protected
8
+ --asset ./doc/css/common.css:css/common.css
9
+ --verbose
10
+ -
11
+ ./doc/*.md
12
+ LICENSE
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org/'
2
+
3
+ gemspec
4
+
5
+ group :testing do
6
+ gem 'rubysl', :platform => :rbx
7
+ end
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2013, Olery <http://olery.com/>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # Oni
2
+
3
+ * [Design](#design)
4
+ * [The Daemon](#the-daemon)
5
+ * [The Mapper](#the-mapper)
6
+ * [The Worker](#the-worker)
7
+ * [Requirements](#requirements)
8
+ * [Installation & Basic Usage](#installation--basic-usage)
9
+ * [License](#license)
10
+
11
+ Oni is a Ruby framework that aims to make it easier to write concurrent daemons
12
+ using a common code structure. Oni itself does not actually daemonize your
13
+ code, manage PID files, resources, etc. Instead you should use Oni in
14
+ combination with other Gems such as [daemon-kit][daemon-kit].
15
+
16
+ Oni was built to standardize the structure amongst the different daemon-kit
17
+ projects used at [Olery][olery]. As time progressed new structures were used
18
+ for new daemons and the old ones were often left as is.
19
+
20
+ Another problem we faced was concurrency. Most daemons were built in a
21
+ single threaded, single processed manner without an easy place to hook some
22
+ kind of concurrency model in.
23
+
24
+ Oni takes care of these problems by providing the following:
25
+
26
+ * A concurrency model in the form of separate worker threads (5 by default).
27
+ * Clear separation of logic into 3 distinctive parts.
28
+ * A common structure for your daemon projects.
29
+
30
+ Oni assumes developers are somewhat familiar with threading and the potential
31
+ issues that may arise, it also assumes that your code doesn't leak large
32
+ amounts of memory over time. Currently there are no plans to include some kind
33
+ of internal resource management system in Oni, this may change in the future.
34
+
35
+ ## Design
36
+
37
+ To understand the design of Oni we'll first look at the typical work flow of a
38
+ daemon:
39
+
40
+ 1. A job gets put in a queue (Amazon SQS).
41
+ 2. Daemon polls queue, takes message.
42
+ 3. Optional message format validation (this was rarely enforced since we had
43
+ control over the input and assumed it to be correct).
44
+ 4. Work gets offloaded to some extra class, in older designs of our daemons it
45
+ would happen in the daemon layer directly.
46
+ 5. Optionally the input data would be modified and re-queued into a separate
47
+ queue. For example, we often pass along data through multiple queues from
48
+ the start until the very end (e.g. batch IDs).
49
+
50
+ Oni tries to make these kind of workflows by breaking them up into 3 different
51
+ layers:
52
+
53
+ 1. A daemon layer tasked with receiving and scheduling work.
54
+ 2. A "mapper" tasked with transforming (and validating) input/output for/from
55
+ the worker. It sits between the daemon and the worker.
56
+ 3. A worker that actually performs a task (asynchronously)
57
+
58
+ Each layer would only do the specific thing it should be doing and would
59
+ offload other work to the next step in the process. The 3 parts are described
60
+ in detail below.
61
+
62
+ ### The Daemon
63
+
64
+ The daemon layer spawns a number of threads that will each receive and perform
65
+ work separately. Typically these workers are long running tasks that poll some
66
+ kind of message queue for jobs to process.
67
+
68
+ In initial iterations Oni used a main job dispatcher (running in the main
69
+ thread) and a separate thread pool for the workers. This proved problematic
70
+ with message queue setups as it would result in the main thread pulling in all
71
+ available jobs and then internally queing them again given there weren't enough
72
+ workers available. This would mean that if the process would crash the messages
73
+ were lost. As a result of this each worker is started in it's own separate
74
+ thread.
75
+
76
+ Comparing Oni with other framework structures one could see the daemon layer as
77
+ a controller (in MVC frameworks), it merely dispatches work to the mapper and
78
+ worker instead of doing everything itself.
79
+
80
+ ### The Mapper
81
+
82
+ The mapper is tasked with two things:
83
+
84
+ 1. Take the input from the daemon, validate it and transform it into a
85
+ structure that the worker can understand.
86
+ 2. Take the resulting output, optionally modify it and pass it back to the
87
+ daemon.
88
+
89
+ The input transformation is put in place to ensure that workers only get data
90
+ that they actually need instead of just receiving the raw message (which may
91
+ include all kinds of meta data completely useless to a worker). It also ensures
92
+ that the input is actually correct before ever passing it to a worker.
93
+
94
+ A typical thing at Olery is that a job gets scheduled and has to pass through
95
+ multiple steps (= daemons) before being completed. Some of these daemons would
96
+ add extra meta-data to the message (e.g. batch IDs, timings, etc) but these
97
+ aren't strictly required to perform an actual job, thus there's no need to pass
98
+ it around several layers deep into your codebase.
99
+
100
+ In the above case the mapper would take care of validating/scrubbing the input
101
+ and adding extra meta-data to the output.
102
+
103
+ ### The Worker
104
+
105
+ The worker would perform the actual work and return some kind of output to the
106
+ daemon. Oni assumes that workers behave reasonably well, currently there's no
107
+ mechanism in place to deal with memory leaks and the likes. Oni also assumes
108
+ that developers are somewhat capable of dealing with asynchronous code since
109
+ all work is performed asynchronously outside of the daemon layer.
110
+
111
+ ## Requirements
112
+
113
+ * Ruby 1.9.3 or newer, preferably an implementation without a GIL such as
114
+ Rubinius or Jruby.
115
+ * Basic understanding of threading/concurrent programming
116
+
117
+ ## Installation & Basic Usage
118
+
119
+ Install the Gem:
120
+
121
+ gem install oni
122
+
123
+ Basic usage of Oni is as following:
124
+
125
+ require 'oni'
126
+
127
+ class MyWorker < Oni::Worker
128
+ def initialize(number)
129
+ @number = number
130
+ end
131
+
132
+ def process
133
+ return @number * 2
134
+ end
135
+ end
136
+
137
+ class MyMapper < Oni::Mapper
138
+ def map_input(input)
139
+ return input[:number]
140
+ end
141
+
142
+ def map_output(output)
143
+ return {:number => output, :completed => Time.now}
144
+ end
145
+ end
146
+
147
+ class MyDaemon < Oni::Daemon
148
+ set :mapper, MyMapper
149
+ set :worker, MyWorker
150
+
151
+ # Here you'd receive your message, e.g. from a queue. We'll use static
152
+ # data as an example.
153
+ def receive
154
+ yield({:number => 10})
155
+ end
156
+
157
+ # This would get executed upon completion of a job.
158
+ def complete(message, result, timings)
159
+ puts result
160
+ end
161
+ end
162
+
163
+ ## License
164
+
165
+ The source code of this repository and Oni itself are licensed under the MIT
166
+ license unless specified otherwise. A copy of this license can be found in the
167
+ file "LICENSE" in the root directory of this repository.
168
+
169
+ [olery]: http://www.olery.com/
170
+ [daemon-kit]: https://github.com/kennethkalmer/daemon-kit
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require_relative 'lib/oni/version'
2
+
3
+ require 'rake/clean'
4
+ require 'bundler/gem_tasks'
5
+ require 'ci/reporter/rake/rspec'
6
+
7
+ CLEAN.include('coverage', 'yardoc')
8
+
9
+ Dir['./task/*.rake'].each do |task|
10
+ import(task)
11
+ end
12
+
13
+ task :default => :test
data/doc/changelog.md ADDED
@@ -0,0 +1,6 @@
1
+ # @title Changelog
2
+ # Changelog
3
+
4
+ ## 1.0.0 - November 18th, 2013
5
+
6
+ The first public release of Oni.
@@ -0,0 +1,69 @@
1
+
2
+ body
3
+ {
4
+ font-size: 14px;
5
+ line-height: 1.6;
6
+ margin: 0 auto;
7
+ max-width: 960px;
8
+ }
9
+
10
+ p code
11
+ {
12
+ background: #f2f2f2;
13
+ padding-left: 3px;
14
+ padding-right: 3px;
15
+ }
16
+
17
+ pre.code
18
+ {
19
+ font-size: 13px;
20
+ line-height: 1.4;
21
+ }
22
+
23
+ /**
24
+ * YARD uses generic table styles, using a special class means those tables
25
+ * don't get messed up.
26
+ */
27
+ .table
28
+ {
29
+ border: 1px solid #ccc;
30
+ border-right: none;
31
+ border-collapse: separate;
32
+ border-spacing: 0;
33
+ text-align: left;
34
+ }
35
+
36
+ .table.full
37
+ {
38
+ width: 100%;
39
+ }
40
+
41
+ .table .field_name
42
+ {
43
+ min-width: 160px;
44
+ }
45
+
46
+ .table thead tr th.no_sort:first-child
47
+ {
48
+ width: 25px;
49
+ }
50
+
51
+ .table thead tr th, .table tbody tr td
52
+ {
53
+ border-bottom: 1px solid #ccc;
54
+ border-right: 1px solid #ccc;
55
+ min-width: 20px;
56
+ padding: 8px 5px;
57
+ text-align: left;
58
+ vertical-align: top;
59
+ }
60
+
61
+ .table tbody tr:last-child td
62
+ {
63
+ border-bottom: none;
64
+ }
65
+
66
+ .table tr:nth-child(odd) td
67
+ {
68
+ background: #f9f9f9;
69
+ }
@@ -0,0 +1,75 @@
1
+ require_relative '../lib/oni'
2
+ require 'net/https'
3
+ require 'json'
4
+
5
+ module GithubStatus
6
+ class Mapper < Oni::Mapper
7
+ def map_input(input)
8
+ return input['url']
9
+ end
10
+
11
+ def map_output(output)
12
+ return output['status']
13
+ end
14
+ end
15
+
16
+ class Worker < Oni::Worker
17
+ attr_reader :url
18
+
19
+ def initialize(url)
20
+ @url = url
21
+ end
22
+
23
+ def process
24
+ uri_object = URI.parse(url)
25
+ http = Net::HTTP.new(uri_object.host, uri_object.port)
26
+ http.use_ssl = true
27
+ request = Net::HTTP::Get.new(uri_object.request_uri)
28
+ response = http.request(request)
29
+
30
+ return JSON(response.body)
31
+ end
32
+ end
33
+
34
+ class Daemon < Oni::Daemon
35
+ # Check GitHub every 10 minutes.
36
+ set :interval, 600
37
+
38
+ # Since this daemon does the same thing over and over we'll only run 1
39
+ # thread instead of multiple ones.
40
+ set :threads, 1
41
+
42
+ # The URL to check.
43
+ set :status_url, 'https://status.github.com/api/status.json'
44
+
45
+ set :mapper, Mapper
46
+ set :worker, Worker
47
+
48
+ def receive
49
+ loop do
50
+ # This is to mimic some kind of job coming from a queue.
51
+ yield({'url' => option(:status_url)})
52
+
53
+ sleep(option(:interval))
54
+ end
55
+ end
56
+
57
+ def complete(message, output, timings)
58
+ sec = timings.real.round(3)
59
+
60
+ puts "GitHub status: #{output}, retrieved in #{sec} seconds"
61
+ end
62
+ end # Daemon
63
+ end # GithubStatus
64
+
65
+ daemon = GithubStatus::Daemon.new
66
+
67
+ %w{INT TERM}.each do |signal|
68
+ trap(signal) do
69
+ puts 'Shutting down...'
70
+
71
+ daemon.stop
72
+ end
73
+ end
74
+
75
+ daemon.start
data/jenkins.sh ADDED
@@ -0,0 +1,16 @@
1
+ # This configuration file is used to test/build the project on Olery's private
2
+ # Jenkins instance. Patches containing changes to this file made by people
3
+ # outside of Olery will most likely be rejected.
4
+
5
+ # The name of the project, used for other settings such as the MySQL database
6
+ # and the package name.
7
+ PROJECT_NAME="oni"
8
+
9
+ # Whether or not to use a MySQL database, set to a non empty value to enable
10
+ # this. Enabling this will tell Jenkins to create/drop the database and to
11
+ # import any migrations if needed.
12
+ unset USE_MYSQL
13
+
14
+ # The command to run for the test suite. Junction itself doesn't have a test
15
+ # suite so we'll use a noop.
16
+ TEST_COMMAND="rake jenkins --trace"