id_service 0.1.1

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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2013 The Ready Project, LLC
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+ of the Software, and to permit persons to whom the Software is furnished to do
9
+ so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,65 @@
1
+ ID Service
2
+ ==========
3
+
4
+ [![Build Status](https://travis-ci.org/readyproject/IDService.png?branch=master)](https://travis-ci.org/readyproject/IDService)
5
+
6
+ This gem provides both client and server classes for running an ID generation service, based on the Thrift framework.
7
+ The overall architecture is based on Twitter's Snowflake service, but written in Ruby. The core ID Generator is very
8
+ fast (10,000 IDs/sec) and the server is designed to be very fast as well (1,000 IDs/sec).
9
+
10
+ **WARNING** :: The IDs returned by the server, whether you use the pre-built one or customize will always return
11
+ sequential and unique IDs, except in the case that you run multiple servers with the same host and worker identifiers.
12
+ In that case you will not be guaranteed uniqueness of the IDs.
13
+
14
+ Running the Server
15
+ ------------------
16
+
17
+ This gem comes with a pre-made server executable that can be used in lieu of building your own server implementation. To
18
+ start the provided server simple run the following command:
19
+
20
+ id_server
21
+
22
+ The server is designed to run in the foreground by default. It also accepts a number of options. You can check the
23
+ available options by passing the `--help` switch to the command.
24
+
25
+ Custom Server
26
+ -------------
27
+
28
+ Some may want to build their own server implementation. This is very simple. Just pass the relevant options when
29
+ initializing an instance of IdService::Server and then call `#serve` on your server instance.
30
+
31
+ require 'id_service/server'
32
+
33
+ options = {
34
+ hostname: 'localhost',
35
+ port: 9000,
36
+ host: 1,
37
+ worker: 1,
38
+ debug: false
39
+ }
40
+
41
+ server = IdService::Server.new(options)
42
+ server.serve
43
+
44
+ That's all there is to it.
45
+
46
+ Using the Client
47
+ ----------------
48
+
49
+ Using the client is just as easy. If your server is running on localhost and port 9000 you can skip supplying the
50
+ options to client initialization.
51
+
52
+ require 'id_service'
53
+
54
+ options = {
55
+ host: 'localhost',
56
+ port: 9000
57
+ }
58
+
59
+ client = IdService::Client.new(options)
60
+ client.open
61
+
62
+ client.get_id
63
+
64
+ The `#get_id` method will always return sequential and unique IDs, given you don't have multiple servers using the same
65
+ host and worker identifiers responding to multiple clients.
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'rubygems'
4
+ require 'id_service/server'
5
+
6
+ opts = OptionParser.new do |opts|
7
+ opts.banner = "IDServer: a Thrift-based ID generation service"
8
+ opts.define_head "Usage: id_server [options]"
9
+ opts.separator ""
10
+ opts.separator "Examples:"
11
+ opts.separator " id_server --port 9000"
12
+ opts.separator " id_server --host 25 --worker 2"
13
+ opts.separator ""
14
+ opts.separator "Options:"
15
+
16
+ opts.on('--host [HOST]') do |v|
17
+ @host = v
18
+ end
19
+
20
+ opts.on('--port [PORT]') do |v|
21
+ @port = v
22
+ end
23
+
24
+ opts.on('--host_id [ID]') do |v|
25
+ @host_id = v
26
+ end
27
+
28
+ opts.on('--worker_id [ID]') do |v|
29
+ @worker_id = v
30
+ end
31
+
32
+ opts.on('--debug') do |v|
33
+ @debug = true unless v.nil?
34
+ end
35
+ end
36
+ opts.parse!
37
+
38
+ options = {
39
+ hostname: @host,
40
+ port: @port,
41
+ host: @host_id,
42
+ worker: @worker_id,
43
+ debug: @debug
44
+ }.delete_if {|key, value| value.nil? }
45
+
46
+ server = IdService::Server.new(options)
47
+ server.serve
@@ -0,0 +1,9 @@
1
+ /* Copyright 2013 The Ready Project, LLC */
2
+
3
+ exception InvalidSystemClock {
4
+ 1: string message,
5
+ }
6
+
7
+ service IdService {
8
+ i64 get_id()
9
+ }
@@ -0,0 +1,78 @@
1
+ require 'active_support/core_ext'
2
+
3
+ #require 'celluloid'
4
+
5
+ require 'id_service/types'
6
+
7
+ class IdGenerator
8
+ #include Celluloid
9
+
10
+ # Project Epoch: Midnight, Mountain Time, January 1st, 2013
11
+ EPOCH = 1357023600
12
+
13
+ # Field Bit Lengths
14
+ TIMESTAMP_BIT_LENGTH = 40
15
+ HOST_ID_BIT_LENGTH = 5
16
+ WORKER_ID_BIT_LENGTH = 5
17
+ SEQUENCE_BIT_LENGTH = 14
18
+
19
+ # Field Shifts
20
+ TIMESTAMP_SHIFT = HOST_ID_BIT_LENGTH + WORKER_ID_BIT_LENGTH + SEQUENCE_BIT_LENGTH
21
+ HOST_ID_SHIFT = WORKER_ID_BIT_LENGTH + SEQUENCE_BIT_LENGTH
22
+ WORKER_ID_SHIFT = SEQUENCE_BIT_LENGTH
23
+
24
+ # Field Masks
25
+ TIMESTAMP_MASK = 0xFFFFFFFFFF000000
26
+ HOST_ID_MASK = 0x0000000000F80000
27
+ WORKER_ID_MASK = 0x000000000007C000
28
+ SEQUENCE_MASK = 0x0000000000003FFF
29
+
30
+ def initialize(options = {})
31
+ options.symbolize_keys!
32
+
33
+ raise ArgumentError, 'missing host id' if options[:host].nil?
34
+ raise ArgumentError, 'missing worker id' if options[:worker].nil?
35
+
36
+ @debug = options[:debug]
37
+ @host = options[:host].to_i
38
+ @worker = options[:worker].to_i
39
+ @sequence = 0
40
+ @last_stamp = -1
41
+
42
+ puts 'Initialized IdGenerator' if @debug
43
+ end
44
+
45
+ def get_id
46
+ now = get_timestamp
47
+
48
+ raise InvalidSystemClock if now < @last_stamp
49
+
50
+ if @last_stamp == now
51
+ @sequence = (@sequence + 1) & SEQUENCE_MASK
52
+
53
+ if @sequence == 0
54
+ sleep_until_next_millisecond
55
+ now = get_timestamp
56
+ end
57
+ else
58
+ @sequence = 0
59
+ end
60
+
61
+ @last_stamp = now
62
+
63
+ id = (now << TIMESTAMP_SHIFT) | (@host << HOST_ID_SHIFT) | (@worker << WORKER_ID_SHIFT) | @sequence
64
+ puts "Generated ID: #{id}" if @debug
65
+ id
66
+ end
67
+
68
+ def get_timestamp
69
+ Time.now.to_i - EPOCH
70
+ end
71
+
72
+ private
73
+
74
+ def sleep_until_next_millisecond
75
+ puts 'Waiting for next milliseconds' if @debug
76
+ false until get_timestamp > @last_stamp
77
+ end
78
+ end
@@ -0,0 +1,4 @@
1
+ require 'id_service/client'
2
+
3
+ module IdService end
4
+
@@ -0,0 +1,55 @@
1
+ require 'thrift'
2
+
3
+ require 'active_support/core_ext'
4
+
5
+ require 'id_service/types'
6
+ require 'id_service/helpers'
7
+
8
+ module IdService
9
+ class Client
10
+ include ::Thrift::Client
11
+
12
+ def initialize(options = {})
13
+ options.symbolize_keys!
14
+ options = default_options.merge(options)
15
+
16
+ @transport = Thrift::BufferedTransport.new(Thrift::Socket.new(options[:host], options[:port]))
17
+ protocol = Thrift::BinaryProtocol.new(@transport)
18
+
19
+ @iprot = protocol
20
+ @oprot = options[:oprot] || protocol
21
+ @seqid = 0
22
+ end
23
+
24
+ def open
25
+ @transport.open
26
+ end
27
+
28
+ def get_id()
29
+ send_get_id()
30
+ return recv_get_id()
31
+ end
32
+
33
+ private
34
+
35
+ def default_options
36
+ {
37
+ host: 'localhost',
38
+ port: 9000,
39
+ oprot: nil
40
+ }
41
+ end
42
+
43
+ def send_get_id()
44
+ send_message('get_id', Get_id_args)
45
+ end
46
+
47
+ def recv_get_id()
48
+ result = receive_message(Get_id_result)
49
+ return result.success unless result.success.nil?
50
+ raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'get_id failed: unknown result')
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,8 @@
1
+ #
2
+ # Autogenerated by Thrift Compiler (0.9.0)
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'thrift'
8
+ require 'id_service/types'
@@ -0,0 +1,34 @@
1
+ require 'thrift'
2
+
3
+ module IdService
4
+ class Get_id_args
5
+ include ::Thrift::Struct, ::Thrift::Struct_Union
6
+
7
+ FIELDS = {
8
+
9
+ }
10
+
11
+ def struct_fields; FIELDS; end
12
+
13
+ def validate
14
+ end
15
+
16
+ ::Thrift::Struct.generate_accessors self
17
+ end
18
+
19
+ class Get_id_result
20
+ include ::Thrift::Struct, ::Thrift::Struct_Union
21
+ SUCCESS = 0
22
+
23
+ FIELDS = {
24
+ SUCCESS => {:type => ::Thrift::Types::I64, :name => 'success'}
25
+ }
26
+
27
+ def struct_fields; FIELDS; end
28
+
29
+ def validate
30
+ end
31
+
32
+ ::Thrift::Struct.generate_accessors self
33
+ end
34
+ end
@@ -0,0 +1,12 @@
1
+ module IdService
2
+ class Processor
3
+ include ::Thrift::Processor
4
+
5
+ def process_get_id(seqid, iprot, oprot)
6
+ args = read_args(iprot, Get_id_args)
7
+ result = Get_id_result.new()
8
+ result.success = @handler.get_id()
9
+ write_result(result, oprot, 'get_id', seqid)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ require 'thrift'
2
+ require 'active_support/core_ext'
3
+
4
+ require 'id_service/helpers'
5
+ require 'id_service/processor'
6
+ require 'id_service/types'
7
+
8
+ require 'id_generator'
9
+
10
+ module IdService
11
+ class Server < Thrift::SimpleServer
12
+ def initialize(options = {})
13
+ options.symbolize_keys!
14
+ options = default_options.merge(options)
15
+
16
+ @handler = IdGenerator.new(options)
17
+ @processor = IdService::Processor.new(@handler)
18
+ @server_transport = Thrift::ServerSocket.new(options[:hostname], options[:port].to_s)
19
+ @transport_factory = Thrift::BufferedTransportFactory.new()
20
+ @protocol_factory = Thrift::BinaryProtocolFactory.new
21
+ end
22
+
23
+ private
24
+ def default_options
25
+ {
26
+ hostname: 'localhost',
27
+ port: 9000,
28
+ host: 1,
29
+ worker: 1,
30
+ debug: false,
31
+ }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ require 'thrift'
2
+
3
+ class InvalidSystemClock < ::Thrift::Exception
4
+ include ::Thrift::Struct, ::Thrift::Struct_Union
5
+ def initialize(message=nil)
6
+ super()
7
+ self.message = message
8
+ end
9
+
10
+ MESSAGE = 1
11
+
12
+ FIELDS = {
13
+ MESSAGE => {:type => ::Thrift::Types::STRING, :name => 'message'}
14
+ }
15
+
16
+ def struct_fields; FIELDS; end
17
+
18
+ def validate
19
+ end
20
+
21
+ ::Thrift::Struct.generate_accessors self
22
+ end
23
+
@@ -0,0 +1,4 @@
1
+ module IdService
2
+ # The version of IdService you are using
3
+ VERSION = '0.1.1'
4
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: id_service
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - James Thompson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thrift
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: activesupport
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: shoulda-matchers
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: simplecov
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: timecop
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description: IdService provides a client and server for setting up a sequential &
127
+ unique id generation service.
128
+ email:
129
+ - jamest@thereadyproject.com
130
+ executables:
131
+ - id_server
132
+ extensions: []
133
+ extra_rdoc_files: []
134
+ files:
135
+ - bin/id_server
136
+ - lib/id_generator.rb
137
+ - lib/id_service/client.rb
138
+ - lib/id_service/constants.rb
139
+ - lib/id_service/helpers.rb
140
+ - lib/id_service/processor.rb
141
+ - lib/id_service/server.rb
142
+ - lib/id_service/types.rb
143
+ - lib/id_service/version.rb
144
+ - lib/id_service.rb
145
+ - ext/IdService.thrift
146
+ - LICENSE
147
+ - README.md
148
+ homepage: http://github.com/readyproject/IDService
149
+ licenses: []
150
+ post_install_message:
151
+ rdoc_options: []
152
+ require_paths:
153
+ - lib
154
+ required_ruby_version: !ruby/object:Gem::Requirement
155
+ none: false
156
+ requirements:
157
+ - - ! '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ segments:
161
+ - 0
162
+ hash: 2474586230224755016
163
+ required_rubygems_version: !ruby/object:Gem::Requirement
164
+ none: false
165
+ requirements:
166
+ - - ! '>='
167
+ - !ruby/object:Gem::Version
168
+ version: 1.3.6
169
+ requirements: []
170
+ rubyforge_project:
171
+ rubygems_version: 1.8.25
172
+ signing_key:
173
+ specification_version: 3
174
+ summary: A client and server for sequential & unique id generation
175
+ test_files: []