oni 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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"