ok_hbase 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +22 -0
- data/examples/README.md +46 -0
- data/examples/advanced/README.md +36 -0
- data/examples/advanced/perf_read.rb +146 -0
- data/examples/advanced/perf_write.rb +143 -0
- data/examples/advanced/table_read.rb +115 -0
- data/examples/advanced/table_write.rb +128 -0
- data/examples/table_scan.rb +97 -0
- data/examples/table_write.rb +97 -0
- data/lib/ok_hbase/active_model.rb +35 -0
- data/lib/ok_hbase/client.rb +42 -0
- data/lib/ok_hbase/concerns/custom_row/class_methods.rb +13 -0
- data/lib/ok_hbase/concerns/custom_row.rb +40 -0
- data/lib/ok_hbase/concerns/indexable/class_methods.rb +13 -0
- data/lib/ok_hbase/concerns/indexable.rb +101 -0
- data/lib/ok_hbase/concerns/row.rb +85 -0
- data/lib/ok_hbase/concerns/table/batch.rb +95 -0
- data/lib/ok_hbase/concerns/table/class_methods.rb +13 -0
- data/lib/ok_hbase/concerns/table/instrumentation.rb +48 -0
- data/lib/ok_hbase/concerns/table.rb +241 -0
- data/lib/ok_hbase/concerns.rb +13 -0
- data/lib/ok_hbase/connection.rb +157 -0
- data/lib/ok_hbase/row.rb +21 -0
- data/lib/ok_hbase/table.rb +10 -0
- data/lib/ok_hbase/version.rb +3 -0
- data/lib/ok_hbase.rb +39 -0
- data/lib/thrift/hbase/hbase.rb +2643 -0
- data/lib/thrift/hbase/hbase_constants.rb +14 -0
- data/lib/thrift/hbase/hbase_types.rb +252 -0
- data/ok-hbase.gemspec +23 -0
- data/spec/ok_hbase/connection_spec.rb +99 -0
- data/spec/ok_hbase/table_spec.rb +149 -0
- data/spec/ok_hbase_spec.rb +24 -0
- data/spec/spec_helper.rb +20 -0
- data/tasks/bump.rb +30 -0
- metadata +122 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
module OkHbase
|
2
|
+
module Concerns
|
3
|
+
module Table
|
4
|
+
class Batch
|
5
|
+
|
6
|
+
attr_accessor :batch_wrapper
|
7
|
+
attr_reader :table, :batch_size, :timestamp
|
8
|
+
|
9
|
+
def initialize(table, timestamp = nil, batch_size = nil, transaction = false, &batch_wrapper)
|
10
|
+
raise TypeError.new "'timestamp' must be an integer or nil" if timestamp && !timestamp.is_a?(Integer)
|
11
|
+
|
12
|
+
if batch_size
|
13
|
+
raise ArgumentError.new "'transaction' cannot be used when 'batch_size' is specified" if transaction
|
14
|
+
raise ValueError.new "'batch_size' must be > 0" unless batch_size > 0
|
15
|
+
end
|
16
|
+
|
17
|
+
@table = table
|
18
|
+
@batch_size = batch_size
|
19
|
+
@timestamp = timestamp
|
20
|
+
@transaction = transaction
|
21
|
+
@batch_wrapper = batch_wrapper
|
22
|
+
@families = nil
|
23
|
+
|
24
|
+
_reset_mutations()
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def send_batch()
|
29
|
+
if batch_wrapper
|
30
|
+
batch_wrapper.call(@mutations) do
|
31
|
+
_send_batch
|
32
|
+
end
|
33
|
+
else
|
34
|
+
_send_batch
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def put(row_key, data)
|
39
|
+
@mutations[row_key] ||= []
|
40
|
+
|
41
|
+
data.each_pair do |column, value|
|
42
|
+
@mutations[row_key] << Apache::Hadoop::Hbase::Thrift::Mutation.new(
|
43
|
+
isDelete: false, column: column, value: value
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
@mutation_count += data.size
|
48
|
+
|
49
|
+
send_batch if @batch_size && @mutation_count > @batch_size
|
50
|
+
end
|
51
|
+
|
52
|
+
def delete(row_key, columns = nil)
|
53
|
+
columns ||= @families ||= @table.send(:_column_family_names)
|
54
|
+
|
55
|
+
@mutations[row_key] ||= []
|
56
|
+
|
57
|
+
columns.each do |column|
|
58
|
+
@mutations[row_key] << Apache::Hadoop::Hbase::Thrift::Mutation.new(isDelete: true, column: column)
|
59
|
+
end
|
60
|
+
|
61
|
+
@mutation_count += columns.size
|
62
|
+
send_batch if @batch_size && @mutation_count > @batch_size
|
63
|
+
end
|
64
|
+
|
65
|
+
def transaction
|
66
|
+
yield self
|
67
|
+
send_batch
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def _reset_mutations
|
73
|
+
@mutations = {}
|
74
|
+
@mutation_count = 0
|
75
|
+
end
|
76
|
+
|
77
|
+
def _send_batch()
|
78
|
+
batch_mutations = @mutations.map do |row_key, mutations|
|
79
|
+
Apache::Hadoop::Hbase::Thrift::BatchMutation.new(row: row_key.dup.force_encoding(Encoding::UTF_8), mutations: mutations)
|
80
|
+
end
|
81
|
+
|
82
|
+
return if batch_mutations.blank?
|
83
|
+
|
84
|
+
if @timestamp
|
85
|
+
@table.connection.client.mutateRowsTs(@table.connection.table_name(@table.table_name), batch_mutations, @timestamp)
|
86
|
+
else
|
87
|
+
@table.connection.client.mutateRows(@table.connection.table_name(@table.table_name), batch_mutations)
|
88
|
+
end
|
89
|
+
|
90
|
+
_reset_mutations()
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module OkHbase
|
2
|
+
module Concerns
|
3
|
+
module Table
|
4
|
+
module Instrumentation
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
instrumented_methods = {
|
9
|
+
load: %w[row rows cells scan],
|
10
|
+
}
|
11
|
+
|
12
|
+
instrumented_methods.each_pair do |method_type, methods|
|
13
|
+
methods.each do |method_to_instrument|
|
14
|
+
module_eval <<-RUBY, __FILE__, __LINE__
|
15
|
+
def #{method_to_instrument}(*args)
|
16
|
+
opts = {
|
17
|
+
name: [table_name, '#{method_type}'].join(' '),
|
18
|
+
description: "#{method_to_instrument}",
|
19
|
+
options: args
|
20
|
+
}
|
21
|
+
ActiveSupport::Notifications.instrument("#{method_type}.ok_hbase", opts) do
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
RUBY
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def batch(timestamp = nil, batch_size = nil, transaction = false)
|
30
|
+
|
31
|
+
batch_wrapper = Proc.new do |*args, &block|
|
32
|
+
opts = {
|
33
|
+
name: [table_name, 'write'].join(' '),
|
34
|
+
description: "send_batch",
|
35
|
+
options: args
|
36
|
+
}
|
37
|
+
ActiveSupport::Notifications.instrument("write.ok_hbase", opts, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
batch = Batch.new(self, timestamp, batch_size, transaction)
|
41
|
+
batch.batch_wrapper = batch_wrapper
|
42
|
+
batch
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'ok_hbase/concerns/table/batch'
|
2
|
+
|
3
|
+
module OkHbase
|
4
|
+
module Concerns
|
5
|
+
module Table
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
SCANNER_DEFAULTS = {
|
9
|
+
start_row: nil,
|
10
|
+
stop_row: nil,
|
11
|
+
row_prefix: nil,
|
12
|
+
columns: nil,
|
13
|
+
filter_string: nil,
|
14
|
+
timestamp: nil,
|
15
|
+
include_timestamp: false,
|
16
|
+
caching: 1000,
|
17
|
+
limit: nil,
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
attr_accessor :table_name, :connection
|
21
|
+
|
22
|
+
def table_name
|
23
|
+
@table_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def table_name=(val)
|
27
|
+
@table_name = val
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.connection
|
31
|
+
@self.connection
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.connection=(val)
|
35
|
+
@self.connection = val
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def families()
|
40
|
+
descriptors = self.connection.client.getColumnDescriptors(self.connection.table_name(table_name))
|
41
|
+
|
42
|
+
families = {}
|
43
|
+
|
44
|
+
descriptors.each_pair do |name, descriptor|
|
45
|
+
name = name[0...-1] # remove trailing ':'
|
46
|
+
families[name] = OkHbase.thrift_type_to_dict(descriptor)
|
47
|
+
end
|
48
|
+
families
|
49
|
+
end
|
50
|
+
|
51
|
+
def regions
|
52
|
+
regions = self.connection.client.getTableRegions(self.connection.table_name(table_name))
|
53
|
+
regions.map { |r| OkHbase.thrift_type_to_dict(r) }
|
54
|
+
end
|
55
|
+
|
56
|
+
def row(row_key, columns = nil, timestamp = nil, include_timestamp = false)
|
57
|
+
raise TypeError.new "'columns' must be a tuple or list" if columns && !columns.is_a?(Array)
|
58
|
+
|
59
|
+
row_key.force_encoding(Encoding::UTF_8)
|
60
|
+
|
61
|
+
rows = if timestamp
|
62
|
+
raise TypeError.new "'timestamp' must be an integer" unless timestamp.is_a? Integer
|
63
|
+
|
64
|
+
self.connection.client.getRowWithColumnsTs(self.connection.table_name(table_name), row_key, columns, timestamp)
|
65
|
+
else
|
66
|
+
self.connection.client.getRowWithColumns(self.connection.table_name(table_name), row_key, columns)
|
67
|
+
end
|
68
|
+
|
69
|
+
rows.empty? ? {} : _make_row(rows[0].columns, include_timestamp)
|
70
|
+
end
|
71
|
+
|
72
|
+
def rows(row_keys, columns = nil, timestamp = nil, include_timestamp = false)
|
73
|
+
raise TypeError.new "'columns' must be a tuple or list" if columns && !columns.is_a?(Array)
|
74
|
+
|
75
|
+
row_keys.map! { |r| r.force_encoding(Encoding::UTF_8) }
|
76
|
+
|
77
|
+
return {} if row_keys.blank?
|
78
|
+
|
79
|
+
rows = if timestamp
|
80
|
+
raise TypeError.new "'timestamp' must be an integer" unless timestamp.is_a? Integer
|
81
|
+
|
82
|
+
columns = _column_family_names() unless columns
|
83
|
+
|
84
|
+
self.connection.client.getRowsWithColumnsTs(self.connection.table_name(table_name), row_keys, columns, timestamp)
|
85
|
+
else
|
86
|
+
self.connection.client.getRowsWithColumns(self.connection.table_name(table_name), row_keys, columns)
|
87
|
+
end
|
88
|
+
|
89
|
+
rows.map { |row| _make_row(row.columns, include_timestamp) }
|
90
|
+
end
|
91
|
+
|
92
|
+
def cells(row_key, column, versions = nil, timestamp = nil, include_timestamp = nil)
|
93
|
+
|
94
|
+
row_key.force_encoding(Encoding::UTF_8)
|
95
|
+
|
96
|
+
versions ||= (2 ** 31) -1
|
97
|
+
|
98
|
+
raise TypeError.new "'versions' parameter must be a number or nil" unless versions.is_a? Integer
|
99
|
+
raise ArgumentError.new "'versions' parameter must be >= 1" unless versions >= 1
|
100
|
+
|
101
|
+
cells = if timestamp
|
102
|
+
raise TypeError.new "'timestamp' must be an integer" unless timestamp.is_a? Integer
|
103
|
+
|
104
|
+
self.connection.client.getVerTs(self.connection.table_name(table_name), row_key, column, timestamp, versions)
|
105
|
+
else
|
106
|
+
self.connection.client.getVer(self.connection.table_name(table_name), row_key, column, versions)
|
107
|
+
end
|
108
|
+
|
109
|
+
cells.map { |cell| include_timestamp ? [cell.value, cell.timestamp] : cell.value }
|
110
|
+
end
|
111
|
+
|
112
|
+
def scan(opts={})
|
113
|
+
|
114
|
+
rows = [] unless block_given?
|
115
|
+
opts = SCANNER_DEFAULTS.merge opts.select { |k| SCANNER_DEFAULTS.keys.include? k }
|
116
|
+
|
117
|
+
|
118
|
+
raise ArgumentError.new "'caching' must be >= 1" unless opts[:caching] && opts[:caching] >= 1
|
119
|
+
raise ArgumentError.new "'limit' must be >= 1" if opts[:limit] && opts[:limit] < 1
|
120
|
+
|
121
|
+
if opts[:row_prefix]
|
122
|
+
raise ArgumentError.new "'row_prefix' cannot be combined with 'start_row' or 'stop_row'" if opts[:start_row] || opts[:stop_row]
|
123
|
+
|
124
|
+
opts[:start_row] = opts[:row_prefix]
|
125
|
+
opts[:stop_row] = OkHbase::increment_string opts[:start_row]
|
126
|
+
|
127
|
+
end
|
128
|
+
opts[:start_row] ||= ''
|
129
|
+
|
130
|
+
scanner = _scanner(opts)
|
131
|
+
|
132
|
+
scanner_id = self.connection.client.scannerOpenWithScan(self.connection.table_name(table_name), scanner)
|
133
|
+
|
134
|
+
fetched_count = returned_count = 0
|
135
|
+
|
136
|
+
begin
|
137
|
+
while true
|
138
|
+
how_many = opts[:limit] ? [opts[:caching], opts[:limit] - returned_count].min : opts[:caching]
|
139
|
+
|
140
|
+
items = if how_many == 1
|
141
|
+
self.connection.client.scannerGet(scanner_id)
|
142
|
+
else
|
143
|
+
self.connection.client.scannerGetList(scanner_id, how_many)
|
144
|
+
end
|
145
|
+
|
146
|
+
fetched_count += items.length
|
147
|
+
|
148
|
+
items.map.with_index do |item, index|
|
149
|
+
if block_given?
|
150
|
+
yield item.row, _make_row(item.columns, opts[:include_timestamp])
|
151
|
+
else
|
152
|
+
rows << [item.row, _make_row(item.columns, opts[:include_timestamp])]
|
153
|
+
end
|
154
|
+
return rows if opts[:limit] && index + 1 + returned_count == opts[:limit]
|
155
|
+
end
|
156
|
+
|
157
|
+
break if items.length < how_many
|
158
|
+
end
|
159
|
+
ensure
|
160
|
+
self.connection.client.scannerClose(scanner_id)
|
161
|
+
end
|
162
|
+
rows
|
163
|
+
end
|
164
|
+
|
165
|
+
def put(row_key, data, timestamp = nil)
|
166
|
+
batch = self.batch(timestamp)
|
167
|
+
|
168
|
+
batch.transaction do |batch|
|
169
|
+
batch.put(row_key, data)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def delete(row_key, columns = nil, timestamp = nil)
|
174
|
+
if columns
|
175
|
+
batch = self.batch(timestamp)
|
176
|
+
batch.transaction do |batch|
|
177
|
+
batch.delete(row_key, columns)
|
178
|
+
end
|
179
|
+
|
180
|
+
else
|
181
|
+
timestamp ? self.connection.client.deleteAllRowTs(self.connection.table_name(table_name), row_key, timestamp) : self.connection.client.deleteAllRow(self.connection.table_name(table_name), row_key)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def batch(timestamp = nil, batch_size = nil, transaction = false)
|
186
|
+
Batch.new(self, timestamp, batch_size, transaction)
|
187
|
+
end
|
188
|
+
|
189
|
+
def counter_get(row_key, column)
|
190
|
+
counter_inc(row_key, column, 0)
|
191
|
+
end
|
192
|
+
|
193
|
+
def counter_set(row_key, column, value = 0)
|
194
|
+
self.batch.transaction do |batch|
|
195
|
+
batch.put(row_key, { column => [value].pack('Q>') })
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def counter_inc(row_key, column, value = 1)
|
200
|
+
self.connection.client.atomicIncrement(self.connection.table_name(table_name), row_key, column, value)
|
201
|
+
end
|
202
|
+
|
203
|
+
def counter_dec(row_key, column, value = 1)
|
204
|
+
counter_inc(row_key, column, -value)
|
205
|
+
end
|
206
|
+
|
207
|
+
alias_method :find, :scan
|
208
|
+
|
209
|
+
def _column_family_names()
|
210
|
+
self.connection.client.getColumnDescriptors(self.connection.table_name(table_name)).keys()
|
211
|
+
end
|
212
|
+
|
213
|
+
def _scanner(opts)
|
214
|
+
scanner = Apache::Hadoop::Hbase::Thrift::TScan.new()
|
215
|
+
scanner_fields = Apache::Hadoop::Hbase::Thrift::TScan::FIELDS
|
216
|
+
|
217
|
+
opts.each_pair do |k, v|
|
218
|
+
const = k.to_s.upcase.gsub('_', '')
|
219
|
+
const_value = Apache::Hadoop::Hbase::Thrift::TScan.const_get(const) rescue nil
|
220
|
+
|
221
|
+
if const_value
|
222
|
+
v.force_encoding(Encoding::UTF_8) if v.is_a?(String)
|
223
|
+
OkHbase.logger.info "setting scanner.#{scanner_fields[const_value][:name]}: #{v}"
|
224
|
+
scanner.send("#{scanner_fields[const_value][:name]}=", v)
|
225
|
+
else
|
226
|
+
end
|
227
|
+
end
|
228
|
+
scanner
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
def _make_row(cell_map, include_timestamp)
|
233
|
+
row = {}
|
234
|
+
cell_map.each_pair do |cell_name, cell|
|
235
|
+
row[cell_name] = include_timestamp ? [cell.value, cell.timestamp] : cell.value
|
236
|
+
end
|
237
|
+
row
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module OkHbase
|
2
|
+
module Concerns
|
3
|
+
|
4
|
+
end
|
5
|
+
end
|
6
|
+
require 'ok_hbase/concerns/table'
|
7
|
+
require 'ok_hbase/concerns/table/class_methods'
|
8
|
+
require 'ok_hbase/concerns/table/instrumentation'
|
9
|
+
require 'ok_hbase/concerns/row'
|
10
|
+
require 'ok_hbase/concerns/custom_row'
|
11
|
+
require 'ok_hbase/concerns/custom_row/class_methods'
|
12
|
+
require 'ok_hbase/concerns/indexable'
|
13
|
+
require 'ok_hbase/concerns/indexable/class_methods'
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'thrift'
|
2
|
+
require 'thrift/transport/socket'
|
3
|
+
require 'thrift/protocol/binary_protocol'
|
4
|
+
|
5
|
+
require 'thrift/hbase/hbase_constants'
|
6
|
+
require 'thrift/hbase/hbase_types'
|
7
|
+
require 'thrift/hbase/hbase'
|
8
|
+
|
9
|
+
require 'ok_hbase/client'
|
10
|
+
|
11
|
+
module OkHbase
|
12
|
+
class Connection
|
13
|
+
|
14
|
+
|
15
|
+
DEFAULT_OPTS = {
|
16
|
+
host: 'localhost',
|
17
|
+
port: 9090,
|
18
|
+
timeout: 5,
|
19
|
+
auto_connect: false,
|
20
|
+
table_prefix: nil,
|
21
|
+
table_prefix_separator: '_',
|
22
|
+
transport: :buffered,
|
23
|
+
max_tries: 3
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
THRIFT_TRANSPORTS = {
|
27
|
+
buffered: Thrift::BufferedTransport,
|
28
|
+
framed: Thrift::FramedTransport,
|
29
|
+
}
|
30
|
+
|
31
|
+
attr_accessor :host, :port, :timeout, :auto_connect, :table_prefix, :table_prefix_separator, :max_tries
|
32
|
+
attr_reader :client
|
33
|
+
|
34
|
+
def initialize(opts={})
|
35
|
+
opts = DEFAULT_OPTS.merge opts
|
36
|
+
|
37
|
+
raise ArgumentError.new ":transport must be one of: #{THRIFT_TRANSPORTS.keys}" unless THRIFT_TRANSPORTS.keys.include?(opts[:transport])
|
38
|
+
raise TypeError.new ":table_prefix must be a string" if opts[:table_prefix] && !opts[:table_prefix].is_a?(String)
|
39
|
+
raise TypeError.new ":table_prefix_separator must be a string" unless opts[:table_prefix_separator].is_a?(String)
|
40
|
+
|
41
|
+
|
42
|
+
@host = opts[:host]
|
43
|
+
@port = opts[:port]
|
44
|
+
@timeout = opts[:timeout]
|
45
|
+
@max_tries = opts[:max_tries]
|
46
|
+
@auto_connect = opts[:auto_connect]
|
47
|
+
@table_prefix = opts[:table_prefix]
|
48
|
+
@table_prefix_separator = opts[:table_prefix_separator]
|
49
|
+
@transport_class = THRIFT_TRANSPORTS[opts[:transport]]
|
50
|
+
|
51
|
+
_refresh_thrift_client
|
52
|
+
open if @auto_connect
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
def open
|
57
|
+
return if open?
|
58
|
+
@transport.open
|
59
|
+
|
60
|
+
OkHbase.logger.info "OkHbase connected"
|
61
|
+
end
|
62
|
+
|
63
|
+
def open?
|
64
|
+
@transport && @transport.open?
|
65
|
+
end
|
66
|
+
|
67
|
+
def close
|
68
|
+
return unless open?
|
69
|
+
@transport.close
|
70
|
+
end
|
71
|
+
|
72
|
+
def table(name, use_prefix=true)
|
73
|
+
name = table_name(name) if use_prefix
|
74
|
+
OkHbase::Table.new(name, self)
|
75
|
+
end
|
76
|
+
|
77
|
+
def tables
|
78
|
+
names = client.getTableNames
|
79
|
+
if table_prefix
|
80
|
+
names = names.map do |n|
|
81
|
+
n["#{table_prefix}#{table_prefix_separator}".size..-1] if n.start_with?(table_prefix)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
names
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_table(name, families)
|
88
|
+
name = table_name(name)
|
89
|
+
|
90
|
+
raise ArgumentError.new "Can't create table #{name}. (no column families specified)" unless families
|
91
|
+
raise TypeError.new "families' arg must be a hash" unless families.respond_to?(:[])
|
92
|
+
|
93
|
+
column_descriptors = []
|
94
|
+
|
95
|
+
families.each_pair do |family_name, options|
|
96
|
+
options ||= {}
|
97
|
+
|
98
|
+
args = {}
|
99
|
+
options.each_pair do |option_name, value|
|
100
|
+
args[option_name.to_s.camelcase(:lower)] = value
|
101
|
+
end
|
102
|
+
|
103
|
+
family_name = "#{family_name}:" unless family_name.to_s.end_with? ':'
|
104
|
+
args[:name] = family_name
|
105
|
+
|
106
|
+
column_descriptors << Apache::Hadoop::Hbase::Thrift::ColumnDescriptor.new(args)
|
107
|
+
end
|
108
|
+
|
109
|
+
client.createTable(name, column_descriptors)
|
110
|
+
table(name)
|
111
|
+
end
|
112
|
+
|
113
|
+
def delete_table(name, disable=false)
|
114
|
+
name = table_name(name)
|
115
|
+
|
116
|
+
disable_table(name) if disable && table_enabled?(name)
|
117
|
+
client.deleteTable(name)
|
118
|
+
end
|
119
|
+
|
120
|
+
def enable_table(name)
|
121
|
+
name = table_name(name)
|
122
|
+
|
123
|
+
client.enableTable(name)
|
124
|
+
end
|
125
|
+
|
126
|
+
def disable_table(name)
|
127
|
+
name = table_name(name)
|
128
|
+
|
129
|
+
client.disableTable(name)
|
130
|
+
end
|
131
|
+
|
132
|
+
def table_enabled?(name)
|
133
|
+
name = table_name(name)
|
134
|
+
|
135
|
+
client.isTableEnabled(name)
|
136
|
+
end
|
137
|
+
|
138
|
+
def compact_table(name, major=false)
|
139
|
+
name = table_name(name)
|
140
|
+
|
141
|
+
major ? client.majorCompact(name) : client.compact(name)
|
142
|
+
end
|
143
|
+
|
144
|
+
def table_name(name)
|
145
|
+
table_prefix && !name.start_with?(table_prefix) ? [table_prefix, name].join(table_prefix_separator) : name
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def _refresh_thrift_client
|
151
|
+
socket = Thrift::Socket.new(host, port, timeout)
|
152
|
+
@transport = @transport_class.new(socket)
|
153
|
+
protocol = Thrift::BinaryProtocolAccelerated.new(@transport)
|
154
|
+
@client = OkHbase::Client.new(protocol, nil, max_tries)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
data/lib/ok_hbase/row.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module OkHbase
|
2
|
+
class Row
|
3
|
+
include OkHbase::Concerns::Row
|
4
|
+
|
5
|
+
def initialize(opts={})
|
6
|
+
opts = opts.with_indifferent_access
|
7
|
+
|
8
|
+
raise ArgumentError.new "'table' must be an OkHBase::Concerns::Table. is a #{opts[:table].class}" unless opts[:table] && opts[:table].is_a?(OkHbase::Concerns::Table)
|
9
|
+
@default_column_family = opts[:default_column_family]
|
10
|
+
|
11
|
+
@table = opts[:table]
|
12
|
+
|
13
|
+
@row_key = opts[:row_key]
|
14
|
+
@raw_data = {}.with_indifferent_access
|
15
|
+
opts[:raw_data].each_pair do |k, v|
|
16
|
+
|
17
|
+
send(:"#{k}=", v)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/ok_hbase.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
|
3
|
+
|
4
|
+
module OkHbase
|
5
|
+
|
6
|
+
mattr_accessor :logger
|
7
|
+
|
8
|
+
def self.root
|
9
|
+
::Pathname.new File.expand_path('../../', __FILE__)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.logger
|
13
|
+
@@logger ||= init_logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.init_logger
|
17
|
+
Logger.new("/dev/null")
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.increment_string(string)
|
21
|
+
bytes = string.bytes.to_a
|
22
|
+
(0...bytes.length).to_a.reverse.each do |i|
|
23
|
+
return (bytes[0...i] << bytes[i]+1).pack('C*').force_encoding(Encoding::UTF_8) unless bytes[i] == 255
|
24
|
+
end
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.thrift_type_to_dict(obj)
|
29
|
+
Hash[obj.class::FIELDS.map{ |k, v| [v[:name].underscore, obj.send(v[:name])]}]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'ok_hbase/version'
|
34
|
+
require 'ok_hbase/client'
|
35
|
+
require 'ok_hbase/connection'
|
36
|
+
require 'ok_hbase/concerns'
|
37
|
+
require 'ok_hbase/table'
|
38
|
+
require 'ok_hbase/row'
|
39
|
+
require 'ok_hbase/active_model'
|