hbase-jruby 0.1.1-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,486 @@
1
+ require 'bigdecimal'
2
+ require 'thread'
3
+
4
+ class HBase
5
+ # @!attribute [r] name
6
+ # @return [String] The name of the table
7
+ class Table
8
+ attr_reader :name
9
+
10
+ include Enumerable
11
+ include Admin
12
+
13
+ # Returns a read-only org.apache.hadoop.hbase.HTableDescriptor object
14
+ # @return [org.apache.hadoop.hbase.client.UnmodifyableHTableDescriptor]
15
+ def descriptor
16
+ htable.get_table_descriptor
17
+ end
18
+
19
+ # Closes the table and returns HTable object back to the HTablePool.
20
+ # @return [nil]
21
+ def close
22
+ Thread.current[:htable] ||= {}
23
+ ht = Thread.current[:htable].delete(@name)
24
+ ht.close if ht
25
+ nil
26
+ end
27
+
28
+ # Checks if the table of the name exists
29
+ # @return [true, false] Whether table exists
30
+ def exists?
31
+ with_admin { |admin| admin.tableExists @name }
32
+ end
33
+
34
+ # Checks if the table is enabled
35
+ # @return [true, false] Whether table is enabled
36
+ def enabled?
37
+ with_admin { |admin| admin.isTableEnabled(@name) }
38
+ end
39
+
40
+ # Checks if the table is disabled
41
+ # @return [true, false] Whether table is disabled
42
+ def disabled?
43
+ !enabled?
44
+ end
45
+
46
+ # @overload create!(column_family_name, props = {})
47
+ # Create the table with one column family of the given name
48
+ # @param [#to_s] The name of the column family
49
+ # @param [Hash] props Table properties
50
+ # @return [nil]
51
+ # @overload create!(column_family_hash, props = {})
52
+ # Create the table with the specified column families
53
+ # @param [Hash] Column family properties
54
+ # @param [Hash] props Table properties
55
+ # @return [nil]
56
+ # @example
57
+ # table.create!(
58
+ # # Column family with default options
59
+ # :cf1 => {},
60
+ # # Another column family with custom properties
61
+ # :cf2 => {
62
+ # :blockcache => true,
63
+ # :blocksize => 128 * 1024,
64
+ # :bloomfilter => :row,
65
+ # :compression => :snappy,
66
+ # :in_memory => true,
67
+ # :keep_deleted_cells => true,
68
+ # :min_versions => 2,
69
+ # :replication_scope => 0,
70
+ # :ttl => 100,
71
+ # :versions => 5
72
+ # }
73
+ # )
74
+ # @overload create!(table_descriptor)
75
+ # Create the table with the given HTableDescriptor
76
+ # @param [org.apache.hadoop.hbase.HTableDescriptor] Table descriptor
77
+ # @return [nil]
78
+ def create! desc, props = {}
79
+ todo = nil
80
+ with_admin do |admin|
81
+ raise RuntimeError, 'Table already exists' if admin.tableExists(@name)
82
+
83
+ case desc
84
+ when HTableDescriptor
85
+ patch_table_descriptor! desc, props
86
+ admin.createTable desc
87
+ when Symbol, String
88
+ todo = lambda { create!({desc => {}}, props) }
89
+ when Hash
90
+ htd = HTableDescriptor.new(@name.to_java_bytes)
91
+ patch_table_descriptor! htd, props
92
+ desc.each do |name, opts|
93
+ htd.addFamily hcd(name, opts)
94
+ end
95
+
96
+ admin.createTable htd
97
+ else
98
+ raise ArgumentError, 'Invalid table description'
99
+ end
100
+ end
101
+ todo.call if todo # Avoids mutex relocking
102
+ end
103
+
104
+ # Alters the table
105
+ # @param [Hash] props Table properties
106
+ # @return [nil]
107
+ # @example
108
+ # table.alter!(
109
+ # :max_filesize => 512 * 1024 ** 2,
110
+ # :memstore_flushsize => 64 * 1024 ** 2,
111
+ # :readonly => false,
112
+ # :deferred_log_flush => true
113
+ # )
114
+ def alter! props
115
+ with_admin do |admin|
116
+ htd = admin.get_table_descriptor(@name.to_java_bytes)
117
+ patch_table_descriptor! htd, props
118
+ while_disabled(admin) do
119
+ admin.modifyTable @name.to_java_bytes, htd
120
+ wait_async_admin(admin)
121
+ end
122
+ end
123
+ end
124
+
125
+ # Adds the column family
126
+ # @param [#to_s] name The name of the column family
127
+ # @param [Hash] opts Column family properties
128
+ # @return [nil]
129
+ def add_family! name, opts
130
+ with_admin do |admin|
131
+ while_disabled(admin) do
132
+ admin.addColumn @name, hcd(name.to_s, opts)
133
+ wait_async_admin(admin)
134
+ end
135
+ end
136
+ end
137
+
138
+ # Alters the column family
139
+ # @param [#to_s] name The name of the column family
140
+ # @param [Hash] opts Column family properties
141
+ # @return [nil]
142
+ def alter_family! name, opts
143
+ with_admin do |admin|
144
+ while_disabled(admin) do
145
+ admin.modifyColumn @name, hcd(name.to_s, opts)
146
+ wait_async_admin(admin)
147
+ end
148
+ end
149
+ end
150
+
151
+ # Removes the column family
152
+ # @param [#to_s] name The name of the column family
153
+ # @return [nil]
154
+ def delete_family! name
155
+ with_admin do |admin|
156
+ while_disabled(admin) do
157
+ admin.deleteColumn @name, name.to_s
158
+ wait_async_admin(admin)
159
+ end
160
+ end
161
+ end
162
+
163
+ # Enables the table
164
+ # @return [nil]
165
+ def enable!
166
+ with_admin do |admin|
167
+ admin.enableTable @name unless admin.isTableEnabled(@name)
168
+ end
169
+ end
170
+
171
+ # Disables the table
172
+ # @return [nil]
173
+ def disable!
174
+ with_admin do |admin|
175
+ admin.disableTable @name if admin.isTableEnabled(@name)
176
+ end
177
+ end
178
+
179
+ # Truncates the table by dropping it and recreating it.
180
+ # @return [nil]
181
+ def truncate!
182
+ htd = htable.get_table_descriptor
183
+ drop!
184
+ create! htd
185
+ end
186
+
187
+ # Drops the table
188
+ # @return [nil]
189
+ def drop!
190
+ with_admin do |admin|
191
+ raise RuntimeError, 'Table does not exist' unless admin.tableExists @name
192
+
193
+ admin.disableTable @name if admin.isTableEnabled(@name)
194
+ admin.deleteTable @name
195
+ close
196
+ end
197
+ end
198
+
199
+ # @overload get(rowkey)
200
+ # Single GET.
201
+ # Gets a record with the given rowkey. If the record is not found, nil is returned.
202
+ # @param [Object] rowkey Rowkey
203
+ # @return [HBase::Result, nil]
204
+ # @overload get(rowkeys)
205
+ # Batch GET. Gets an array of records with the given rowkeys.
206
+ # Nonexistent records will be returned as nils.
207
+ # @param [Array<Object>] rowkeys Rowkeys
208
+ # @return [Array<HBase::Result>]
209
+ def get rowkeys
210
+ each.get rowkeys
211
+ end
212
+
213
+ # @overload put(rowkey, data)
214
+ # Put operation on a rowkey
215
+ # @param [Object] rowkey Rowkey
216
+ # @param [Hash] data Data to put
217
+ # @return [Fixnum] Number of puts succeeded
218
+ # @overload put(data)
219
+ # Put operation on multiple rowkeys
220
+ # @param [Hash<Hash>] data Data to put indexed by rowkeys
221
+ # @return [Fixnum] Number of puts succeeded
222
+ def put *args
223
+ return put(args.first => args.last) if args.length == 2
224
+
225
+ puts = args.first.map { |rowkey, props| putify rowkey, props }
226
+ htable.put puts
227
+ puts.length
228
+ end
229
+
230
+ # @overload delete(rowkey)
231
+ # Deletes a row with the given rowkey
232
+ # @param [Object] rowkey
233
+ # @return [nil]
234
+ # @example
235
+ # table.delete('a000')
236
+ # @overload delete(rowkey, column_family)
237
+ # Deletes columns with the given column family from the row
238
+ # @param [Object] rowkey
239
+ # @param [String] column_family
240
+ # @return [nil]
241
+ # @example
242
+ # table.delete('a000', 'cf1')
243
+ # @overload delete(rowkey, column)
244
+ # Deletes a column
245
+ # @param [Object] rowkey
246
+ # @param [String, Array] column Column expression in String "FAMILY:QUALIFIER", or in Array [FAMILY, QUALIFIER]
247
+ # @return [nil]
248
+ # @example
249
+ # table.delete('a000', 'cf1:col1')
250
+ # @overload delete(rowkey, column, timestamp)
251
+ # Deletes a version of a column
252
+ # @param [Object] rowkey
253
+ # @param [String, Array] column Column expression in String "FAMILY:QUALIFIER", or in Array [FAMILY, QUALIFIER]
254
+ # @param [Fixnum] timestamp Timestamp.
255
+ # @return [nil]
256
+ # @example
257
+ # table.delete('a000', 'cf1:col1', 1352978648642)
258
+ # @overload delete(*delete_specs)
259
+ # Batch deletion
260
+ # @param [Array<Array>] delete_specs
261
+ # @return [nil]
262
+ # @example
263
+ # table.delete(
264
+ # ['a000', 'cf1:col1', 1352978648642],
265
+ # ['a001', 'cf1:col1'],
266
+ # ['a002', 'cf1'],
267
+ # ['a003'])
268
+ def delete *args
269
+ specs = args.first.is_a?(Array) ? args : [args]
270
+
271
+ htable.delete specs.map { |spec|
272
+ rowkey, cfcq, *ts = spec
273
+ cf, cq = Util.parse_column_name(cfcq) if cfcq
274
+
275
+ Delete.new(Util.to_bytes rowkey).tap { |del|
276
+ if !ts.empty?
277
+ ts.each do |t|
278
+ del.deleteColumn cf, cq, t
279
+ end
280
+ elsif cq
281
+ # Delete all versions
282
+ del.deleteColumns cf, cq
283
+ elsif cf
284
+ del.deleteFamily cf
285
+ end
286
+ }
287
+ }
288
+ end
289
+
290
+ # @overload increment(rowkey, column, by)
291
+ # Atomically increase column value by the specified amount
292
+ # @param [Object] rowkey Rowkey
293
+ # @param [String, Array] column Column expression in String "FAMILY:QUALIFIER", or in Array [FAMILY, QUALIFIER]
294
+ # @param [Fixnum] by Increment amount
295
+ # @return [Fixnum] Column value after increment
296
+ # @example
297
+ # table.increment('a000', 'cf1:col1', 1)
298
+ # @overload increment(rowkey, column_by_hash)
299
+ # Atomically increase values of multiple columns
300
+ # @param [Object] rowkey Rowkey
301
+ # @param [Hash] column_by_hash Column expression to increment amount pairs
302
+ # @example
303
+ # table.increment('a000', 'cf1:col1' => 1, 'cf1:col2' => 2)
304
+ def increment rowkey, *args
305
+ if args.first.is_a?(Hash)
306
+ cols = args.first
307
+ htable.increment Increment.new(Util.to_bytes rowkey).tap { |inc|
308
+ cols.each do |col, by|
309
+ cf, cq = Util.parse_column_name(col)
310
+ inc.addColumn cf, cq, by
311
+ end
312
+ }
313
+ else
314
+ col, by = args
315
+ cf, cq = Util.parse_column_name(col)
316
+ htable.incrementColumnValue Util.to_bytes(rowkey), cf, cq, by || 1
317
+ end
318
+ end
319
+
320
+ # Returns the count of the rows in the table
321
+ # @return [Fixnum]
322
+ def count
323
+ each.count
324
+ end
325
+
326
+ # Scan through the table
327
+ # @yield [HBase::Result] Yields each row in the scope
328
+ # @return [HBase::Scoped]
329
+ def each
330
+ if block_given?
331
+ Scoped.send(:new, self).each { |r| yield r }
332
+ else
333
+ Scoped.send(:new, self)
334
+ end
335
+ end
336
+
337
+ # @see HBase::Scoped#range
338
+ # @return [HBase::Scoped]
339
+ def range *key_range
340
+ each.range(*key_range)
341
+ end
342
+
343
+ # @see HBase::Scoped#project
344
+ # @return [HBase::Scoped]
345
+ def project *columns
346
+ each.project(*columns)
347
+ end
348
+
349
+ # @see HBase::Scoped#filter
350
+ # @return [HBase::Scoped]
351
+ def filter *filters
352
+ each.filter(*filters)
353
+ end
354
+
355
+ # @see HBase::Scoped#limit
356
+ # @return [HBase::Scoped]
357
+ def limit rows
358
+ each.limit rows
359
+ end
360
+
361
+ # @see HBase::Scoped#versions
362
+ # @return [HBase::Scoped]
363
+ def versions vs
364
+ each.versions vs
365
+ end
366
+
367
+ # @see HBase::Scoped#caching
368
+ # @return [HBase::Scoped]
369
+ def caching rows
370
+ each.caching rows
371
+ end
372
+
373
+ # @see HBase::Scoped#batch
374
+ # @return [HBase::Scoped]
375
+ def batch b
376
+ each.batch b
377
+ end
378
+
379
+ # Returns the underlying org.apache.hadoop.hbase.client.HTable object (local to current thread)
380
+ # @return [org.apache.hadoop.hbase.client.HTable]
381
+ def htable
382
+ # @htable ||= @pool.get_table(@name)
383
+ (local_htables = Thread.current[:htable] ||= {})[@name] ||
384
+ (local_htables[@name] = @pool.get_table(@name))
385
+ end
386
+
387
+ # Returns table description
388
+ # @return [String] Table description
389
+ def inspect
390
+ if exists?
391
+ htable.get_table_descriptor.to_s
392
+ else
393
+ # FIXME
394
+ "{NAME => '#{@name}'}"
395
+ end
396
+ end
397
+
398
+ private
399
+ def initialize config, htable_pool, name
400
+ @config = config
401
+ @pool = htable_pool
402
+ @name = name.to_s
403
+ @htable = nil
404
+ end
405
+
406
+ def while_disabled admin
407
+ begin
408
+ admin.disableTable @name if admin.isTableEnabled(@name)
409
+ yield
410
+ ensure
411
+ admin.enableTable @name
412
+ end
413
+ end
414
+
415
+ def putify rowkey, props
416
+ Put.new(Util.to_bytes rowkey).tap { |put|
417
+ props.each do |col, val|
418
+ cf, cq = Util.parse_column_name(col)
419
+ put.add cf, cq, Util.to_bytes(val)
420
+ end
421
+ }
422
+ end
423
+
424
+ def hcd name, opts
425
+ method_map = {
426
+ :blockcache => :setBlockCacheEnabled,
427
+ :blocksize => :setBlocksize,
428
+ :bloomfilter => :setBloomFilterType,
429
+ :compression => :setCompressionType,
430
+ :data_block_encoding => :setDataBlockEncoding,
431
+ :encode_on_disk => :setEncodeOnDisk,
432
+ :in_memory => :setInMemory,
433
+ :keep_deleted_cells => :setKeepDeletedCells,
434
+ :min_versions => :setMinVersions,
435
+ :replication_scope => :setScope,
436
+ :ttl => :setTimeToLive,
437
+ :versions => :setMaxVersions,
438
+ }
439
+ HColumnDescriptor.new(name.to_s).tap do |hcd|
440
+ opts.each do |key, val|
441
+ if method_map[key]
442
+ hcd.send method_map[key],
443
+ ({
444
+ :bloomfilter => proc { |v|
445
+ const_shortcut StoreFile::BloomType, v, "Invalid bloom filter type"
446
+ },
447
+ :compression => proc { |v|
448
+ const_shortcut Compression::Algorithm, v, "Invalid compression algorithm"
449
+ }
450
+ }[key] || proc { |a| a }).call(val)
451
+ else
452
+ raise ArgumentError, "Invalid option: #{key}"
453
+ end
454
+ end#opts
455
+ end
456
+ end
457
+
458
+ def const_shortcut base, v, message
459
+ vs = v.to_s.upcase.to_sym
460
+ #if base.constants.map { |c| base.const_get c }.include?(v)
461
+ if base.constants.map { |c| base.const_get c }.any? { |cv| v == cv }
462
+ v
463
+ elsif base.constants.include? vs
464
+ base.const_get vs
465
+ else
466
+ raise ArgumentError, [message, v.to_s].join(': ')
467
+ end
468
+ end
469
+
470
+ def patch_table_descriptor! htd, props
471
+ props.each do |key, value|
472
+ method = {
473
+ :max_filesize => :setMaxFileSize,
474
+ :readonly => :setReadOnly,
475
+ :memstore_flushsize => :setMemStoreFlushSize,
476
+ :deferred_log_flush => :setDeferredLogFlush
477
+ }[key]
478
+ raise ArgumentError, "Invalid table property: #{key}" unless method
479
+
480
+ htd.send method, value
481
+ end
482
+ htd
483
+ end
484
+ end#Table
485
+ end#HBase
486
+