cassandra-driver 1.0.0.beta.3 → 1.0.0.rc.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.
Files changed (53) hide show
  1. checksums.yaml +13 -5
  2. data/README.md +8 -6
  3. data/lib/cassandra.rb +99 -13
  4. data/lib/cassandra/address_resolution.rb +36 -0
  5. data/lib/cassandra/address_resolution/policies.rb +2 -0
  6. data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +56 -0
  7. data/lib/cassandra/address_resolution/policies/none.rb +35 -0
  8. data/lib/cassandra/auth.rb +1 -1
  9. data/lib/cassandra/auth/providers/password.rb +1 -1
  10. data/lib/cassandra/client.rb +2 -2
  11. data/lib/cassandra/client/batch.rb +3 -3
  12. data/lib/cassandra/client/client.rb +12 -12
  13. data/lib/cassandra/client/connection_manager.rb +2 -2
  14. data/lib/cassandra/client/connector.rb +6 -10
  15. data/lib/cassandra/client/prepared_statement.rb +4 -4
  16. data/lib/cassandra/client/request_runner.rb +1 -2
  17. data/lib/cassandra/cluster.rb +15 -5
  18. data/lib/cassandra/cluster/client.rb +158 -96
  19. data/lib/cassandra/cluster/connector.rb +42 -27
  20. data/lib/cassandra/cluster/control_connection.rb +384 -132
  21. data/lib/cassandra/cluster/options.rb +5 -2
  22. data/lib/cassandra/cluster/registry.rb +19 -9
  23. data/lib/cassandra/compression.rb +1 -1
  24. data/lib/cassandra/compression/compressors/lz4.rb +1 -1
  25. data/lib/cassandra/compression/compressors/snappy.rb +1 -1
  26. data/lib/cassandra/driver.rb +28 -20
  27. data/lib/cassandra/errors.rb +325 -35
  28. data/lib/cassandra/future.rb +3 -3
  29. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +7 -3
  30. data/lib/cassandra/load_balancing/policies/token_aware.rb +1 -1
  31. data/lib/cassandra/protocol.rb +0 -16
  32. data/lib/cassandra/protocol/cql_byte_buffer.rb +18 -18
  33. data/lib/cassandra/protocol/cql_protocol_handler.rb +74 -8
  34. data/lib/cassandra/protocol/frame_decoder.rb +2 -2
  35. data/lib/cassandra/protocol/frame_encoder.rb +1 -1
  36. data/lib/cassandra/protocol/response.rb +1 -1
  37. data/lib/cassandra/protocol/responses/detailed_error_response.rb +16 -1
  38. data/lib/cassandra/protocol/responses/error_response.rb +17 -0
  39. data/lib/cassandra/protocol/responses/event_response.rb +1 -1
  40. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +1 -1
  41. data/lib/cassandra/protocol/responses/result_response.rb +1 -1
  42. data/lib/cassandra/protocol/responses/rows_result_response.rb +1 -1
  43. data/lib/cassandra/protocol/type_converter.rb +4 -3
  44. data/lib/cassandra/reconnection.rb +1 -1
  45. data/lib/cassandra/retry.rb +3 -5
  46. data/lib/cassandra/session.rb +11 -5
  47. data/lib/cassandra/table.rb +1 -1
  48. data/lib/cassandra/time_uuid.rb +21 -83
  49. data/lib/cassandra/util.rb +1 -1
  50. data/lib/cassandra/uuid.rb +6 -4
  51. data/lib/cassandra/uuid/generator.rb +207 -0
  52. data/lib/cassandra/version.rb +1 -1
  53. metadata +24 -19
@@ -188,7 +188,7 @@ module Cassandra
188
188
  monitor = Monitor.new
189
189
  promise = Promise.new
190
190
  remaining = futures.length
191
- values = Array.new(length)
191
+ values = Array.new(remaining)
192
192
 
193
193
  futures.each_with_index do |future, i|
194
194
  future.on_complete do |v, e|
@@ -390,11 +390,11 @@ module Cassandra
390
390
  end
391
391
 
392
392
  def success(value)
393
- @block.call(nil, value)
393
+ @block.call(value, nil)
394
394
  end
395
395
 
396
396
  def failure(error)
397
- @block.call(error, nil)
397
+ @block.call(nil, error)
398
398
  end
399
399
  end
400
400
 
@@ -56,11 +56,11 @@ module Cassandra
56
56
 
57
57
  include MonitorMixin
58
58
 
59
- def initialize(datacenter, max_remote_hosts_to_use = nil, use_remote_hosts_for_local_consistency = false)
60
- datacenter = String(datacenter)
59
+ def initialize(datacenter = nil, max_remote_hosts_to_use = nil, use_remote_hosts_for_local_consistency = false)
60
+ datacenter = datacenter && String(datacenter)
61
61
  max_remote_hosts_to_use = max_remote_hosts_to_use && Integer(max_remote_hosts_to_use)
62
62
 
63
- raise ::ArgumentError, "datacenter cannot be nil" if datacenter.nil?
63
+ raise ::ArgumentError, "datacenter cannot be empty" if datacenter && datacenter.empty?
64
64
  raise ::ArgumentError, "max_remote_hosts_to_use must be nil or >= 0" if max_remote_hosts_to_use && max_remote_hosts_to_use < 0
65
65
 
66
66
  @datacenter = datacenter
@@ -75,6 +75,10 @@ module Cassandra
75
75
  end
76
76
 
77
77
  def host_up(host)
78
+ if !@datacenter && host.datacenter
79
+ @datacenter = host.datacenter
80
+ end
81
+
78
82
  if host.datacenter.nil? || host.datacenter == @datacenter
79
83
  synchronize { @local = @local.dup.push(host) }
80
84
  else
@@ -111,7 +111,7 @@ module Cassandra
111
111
  replicas = @cluster.find_replicas(keyspace, statement)
112
112
  return @policy.plan(keyspace, statement, options) if replicas.empty?
113
113
 
114
- Plan.new(replicas.dup, @policy, keyspace, statement, options)
114
+ Plan.new(replicas.shuffle, @policy, keyspace, statement, options)
115
115
  end
116
116
  end
117
117
  end
@@ -17,24 +17,8 @@
17
17
  #++
18
18
 
19
19
  module Cassandra
20
- # @private
21
- ProtocolError = Class.new(Error)
22
-
23
20
  # @private
24
21
  module Protocol
25
- DecodingError = Class.new(ProtocolError)
26
- EncodingError = Class.new(ProtocolError)
27
- InvalidStreamIdError = Class.new(ProtocolError)
28
- InvalidValueError = Class.new(ProtocolError)
29
- UnsupportedOperationError = Class.new(ProtocolError)
30
- UnsupportedFrameTypeError = Class.new(ProtocolError)
31
- UnsupportedResultKindError = Class.new(ProtocolError)
32
- UnsupportedColumnTypeError = Class.new(ProtocolError)
33
- UnsupportedEventTypeError = Class.new(ProtocolError)
34
- UnsupportedFeatureError = Class.new(ProtocolError)
35
- UnexpectedCompressionError = Class.new(ProtocolError)
36
- UnmaterializedRowsError = Class.new(ProtocolError)
37
-
38
22
  module Formats
39
23
  CHAR_FORMAT = 'c'.freeze
40
24
  DOUBLE_FORMAT = 'G'.freeze
@@ -22,7 +22,7 @@ module Cassandra
22
22
  def read_unsigned_byte
23
23
  read_byte
24
24
  rescue RangeError => e
25
- raise DecodingError, e.message, e.backtrace
25
+ raise Errors::DecodingError, e.message, e.backtrace
26
26
  end
27
27
 
28
28
  def read_varint(len=bytesize, signed=true)
@@ -36,7 +36,7 @@ module Cassandra
36
36
  end
37
37
  n
38
38
  rescue RangeError => e
39
- raise DecodingError, e.message, e.backtrace
39
+ raise Errors::DecodingError, e.message, e.backtrace
40
40
  end
41
41
 
42
42
  def read_decimal(len=bytesize)
@@ -57,8 +57,8 @@ module Cassandra
57
57
  fraction_string << number_string[number_string.length - size, number_string.length]
58
58
  end
59
59
  BigDecimal.new(fraction_string)
60
- rescue DecodingError => e
61
- raise DecodingError, e.message, e.backtrace
60
+ rescue Errors::DecodingError => e
61
+ raise Errors::DecodingError, e.message, e.backtrace
62
62
  end
63
63
 
64
64
  def read_long
@@ -68,19 +68,19 @@ module Cassandra
68
68
  bottom ^= 0xffffffff
69
69
  -((top << 32) | bottom) - 1
70
70
  rescue RangeError => e
71
- raise DecodingError, e.message, e.backtrace
71
+ raise Errors::DecodingError, e.message, e.backtrace
72
72
  end
73
73
 
74
74
  def read_double
75
75
  read(8).unpack(Formats::DOUBLE_FORMAT).first
76
76
  rescue RangeError => e
77
- raise DecodingError, "Not enough bytes available to decode a double: #{e.message}", e.backtrace
77
+ raise Errors::DecodingError, "Not enough bytes available to decode a double: #{e.message}", e.backtrace
78
78
  end
79
79
 
80
80
  def read_float
81
81
  read(4).unpack(Formats::FLOAT_FORMAT).first
82
82
  rescue RangeError => e
83
- raise DecodingError, "Not enough bytes available to decode a float: #{e.message}", e.backtrace
83
+ raise Errors::DecodingError, "Not enough bytes available to decode a float: #{e.message}", e.backtrace
84
84
  end
85
85
 
86
86
  def read_signed_int
@@ -88,13 +88,13 @@ module Cassandra
88
88
  return n if n <= 0x7fffffff
89
89
  n - 0xffffffff - 1
90
90
  rescue RangeError => e
91
- raise DecodingError, "Not enough bytes available to decode an int: #{e.message}", e.backtrace
91
+ raise Errors::DecodingError, "Not enough bytes available to decode an int: #{e.message}", e.backtrace
92
92
  end
93
93
 
94
94
  def read_unsigned_short
95
95
  read_short
96
96
  rescue RangeError => e
97
- raise DecodingError, "Not enough bytes available to decode a short: #{e.message}", e.backtrace
97
+ raise Errors::DecodingError, "Not enough bytes available to decode a short: #{e.message}", e.backtrace
98
98
  end
99
99
 
100
100
  def read_string
@@ -103,7 +103,7 @@ module Cassandra
103
103
  string.force_encoding(::Encoding::UTF_8)
104
104
  string
105
105
  rescue RangeError => e
106
- raise DecodingError, "Not enough bytes available to decode a string: #{e.message}", e.backtrace
106
+ raise Errors::DecodingError, "Not enough bytes available to decode a string: #{e.message}", e.backtrace
107
107
  end
108
108
 
109
109
  def read_long_string
@@ -112,13 +112,13 @@ module Cassandra
112
112
  string.force_encoding(::Encoding::UTF_8)
113
113
  string
114
114
  rescue RangeError => e
115
- raise DecodingError, "Not enough bytes available to decode a long string: #{e.message}", e.backtrace
115
+ raise Errors::DecodingError, "Not enough bytes available to decode a long string: #{e.message}", e.backtrace
116
116
  end
117
117
 
118
118
  def read_uuid(impl=Uuid)
119
119
  impl.new(read_varint(16, false))
120
- rescue DecodingError => e
121
- raise DecodingError, "Not enough bytes available to decode a UUID: #{e.message}", e.backtrace
120
+ rescue Errors::DecodingError => e
121
+ raise Errors::DecodingError, "Not enough bytes available to decode a UUID: #{e.message}", e.backtrace
122
122
  end
123
123
 
124
124
  def read_string_list
@@ -131,7 +131,7 @@ module Cassandra
131
131
  return nil if size & 0x80000000 == 0x80000000
132
132
  read(size)
133
133
  rescue RangeError => e
134
- raise DecodingError, "Not enough bytes available to decode a bytes: #{e.message}", e.backtrace
134
+ raise Errors::DecodingError, "Not enough bytes available to decode a bytes: #{e.message}", e.backtrace
135
135
  end
136
136
 
137
137
  def read_short_bytes
@@ -139,7 +139,7 @@ module Cassandra
139
139
  return nil if size & 0x8000 == 0x8000
140
140
  read(size)
141
141
  rescue RangeError => e
142
- raise DecodingError, "Not enough bytes available to decode a short bytes: #{e.message}", e.backtrace
142
+ raise Errors::DecodingError, "Not enough bytes available to decode a short bytes: #{e.message}", e.backtrace
143
143
  end
144
144
 
145
145
  def read_option
@@ -157,12 +157,12 @@ module Cassandra
157
157
  port = read_int
158
158
  [ip_addr, port]
159
159
  rescue RangeError => e
160
- raise DecodingError, "Not enough bytes available to decode an INET: #{e.message}", e.backtrace
160
+ raise Errors::DecodingError, "Not enough bytes available to decode an INET: #{e.message}", e.backtrace
161
161
  end
162
162
 
163
163
  def read_consistency
164
164
  index = read_unsigned_short
165
- raise DecodingError, "Unknown consistency index #{index}" if index >= CONSISTENCIES.size || CONSISTENCIES[index].nil?
165
+ raise Errors::DecodingError, "Unknown consistency index #{index}" if index >= CONSISTENCIES.size || CONSISTENCIES[index].nil?
166
166
  CONSISTENCIES[index]
167
167
  end
168
168
 
@@ -241,7 +241,7 @@ module Cassandra
241
241
 
242
242
  def append_consistency(consistency)
243
243
  index = CONSISTENCIES.index(consistency)
244
- raise EncodingError, %(Unknown consistency "#{consistency}") if index.nil? || CONSISTENCIES[index].nil?
244
+ raise Errors::EncodingError, %(Unknown consistency "#{consistency}") if index.nil? || CONSISTENCIES[index].nil?
245
245
  append_short(index)
246
246
  end
247
247
 
@@ -35,7 +35,7 @@ module Cassandra
35
35
  # @return [String] the current keyspace for the underlying connection
36
36
  attr_reader :keyspace
37
37
 
38
- def initialize(connection, scheduler, protocol_version, compressor=nil)
38
+ def initialize(connection, scheduler, protocol_version, compressor=nil, heartbeat_interval = 30, idle_timeout = 60)
39
39
  @connection = connection
40
40
  @scheduler = scheduler
41
41
  @compressor = compressor
@@ -53,6 +53,10 @@ module Cassandra
53
53
  @lock = Mutex.new
54
54
  @closed_promise = Ione::Promise.new
55
55
  @keyspace = nil
56
+ @heartbeat = nil
57
+ @terminate = nil
58
+ @heartbeat_interval = heartbeat_interval
59
+ @idle_timeout = idle_timeout
56
60
  end
57
61
 
58
62
  # Returns the hostname of the underlying connection
@@ -128,7 +132,7 @@ module Cassandra
128
132
  # closes the futures of all active requests will be failed with the error
129
133
  # that caused the connection to close, or nil.
130
134
  #
131
- # When `timeout` is specified the future will fail with {Cassandra::TimeoutError}
135
+ # When `timeout` is specified the future will fail with {Cassandra::Errors::TimeoutError}
132
136
  # after that many seconds have passed. If a response arrives after that
133
137
  # time it will be lost. If a response never arrives for the request the
134
138
  # channel occupied by the request will _not_ be reused.
@@ -138,8 +142,9 @@ module Cassandra
138
142
  # failing the request
139
143
  # @return [Ione::Future<Cassandra::Protocol::Response>] a future that resolves to
140
144
  # the response
141
- def send_request(request, timeout=nil)
142
- return Ione::Future.failed(Errors::NotConnectedError.new) if closed?
145
+ def send_request(request, timeout=nil, with_heartbeat = true)
146
+ return Ione::Future.failed(Errors::IOError.new('Connection closed')) if closed?
147
+ schedule_heartbeat if with_heartbeat
143
148
  promise = RequestPromise.new(request, @frame_encoder)
144
149
  id = nil
145
150
  @lock.lock
@@ -174,8 +179,18 @@ module Cassandra
174
179
  # Closes the underlying connection.
175
180
  #
176
181
  # @return [Ione::Future] a future that completes when the connection has closed
177
- def close
178
- @connection.close
182
+ def close(cause = nil)
183
+ if @heartbeat
184
+ @scheduler.cancel_timer(@heartbeat)
185
+ @heartbeat = nil
186
+ end
187
+
188
+ if @terminate
189
+ @scheduler.cancel_timer(@terminate)
190
+ @terminate = nil
191
+ end
192
+
193
+ @connection.close(cause)
179
194
  @closed_promise.future
180
195
  end
181
196
 
@@ -199,7 +214,7 @@ module Cassandra
199
214
  def time_out!
200
215
  unless future.completed?
201
216
  @timed_out = true
202
- fail(TimeoutError.new)
217
+ fail(Errors::TimeoutError.new('Timed out'))
203
218
  end
204
219
  end
205
220
 
@@ -209,6 +224,7 @@ module Cassandra
209
224
  end
210
225
 
211
226
  def receive_data(data)
227
+ reschedule_termination
212
228
  @read_buffer << data
213
229
  @current_frame = @frame_decoder.decode_frame(@read_buffer, @current_frame)
214
230
  while @current_frame.complete?
@@ -248,6 +264,9 @@ module Cassandra
248
264
  if response.is_a?(Protocol::SetKeyspaceResultResponse)
249
265
  @keyspace = response.keyspace
250
266
  end
267
+ if response.is_a?(Protocol::SchemaChangeResultResponse) && response.change == 'DROPPED' && response.keyspace == @keyspace && response.table.empty?
268
+ @keyspace = nil
269
+ end
251
270
  flush_request_queue
252
271
  unless promise.timed_out?
253
272
  promise.fulfill(response)
@@ -291,9 +310,22 @@ module Cassandra
291
310
  end
292
311
 
293
312
  def socket_closed(cause)
294
- request_failure_cause = cause || Io::ConnectionClosedError.new
313
+ if cause
314
+ e = Errors::IOError.new(cause.message)
315
+ e.set_backtrace(cause.backtrace)
316
+
317
+ cause = e
318
+ end
319
+
320
+ request_failure_cause = cause || Errors::IOError.new('Connection closed')
295
321
  promises_to_fail = nil
296
322
  @lock.synchronize do
323
+ @scheduler.cancel_timer(@heartbeat) if @heartbeat
324
+ @scheduler.cancel_timer(@terminate) if @terminate
325
+
326
+ @heartbeat = nil
327
+ @terminate = nil
328
+
297
329
  promises_to_fail = @promises.compact
298
330
  promises_to_fail.concat(@request_queue_in)
299
331
  promises_to_fail.concat(@request_queue_out)
@@ -311,6 +343,37 @@ module Cassandra
311
343
  end
312
344
  end
313
345
 
346
+ def schedule_heartbeat
347
+ timer = nil
348
+
349
+ @lock.synchronize do
350
+ @scheduler.cancel_timer(@heartbeat) if @heartbeat && !@heartbeat.resolved?
351
+
352
+ @heartbeat = timer = @scheduler.schedule_timer(@heartbeat_interval)
353
+ end
354
+
355
+ timer.on_value do
356
+ send_request(HEARTBEAT, nil, false).on_value do
357
+ schedule_heartbeat
358
+ end
359
+ end
360
+ end
361
+
362
+ def reschedule_termination
363
+ timer = nil
364
+
365
+ @lock.synchronize do
366
+ @scheduler.cancel_timer(@terminate) if @terminate
367
+
368
+ @terminate = timer = @scheduler.schedule_timer(@idle_timeout)
369
+ end
370
+
371
+ timer.on_value do
372
+ @terminate = nil
373
+ @connection.close(TERMINATED)
374
+ end
375
+ end
376
+
314
377
  def next_stream_id
315
378
  if (stream_id = @promises.index(nil))
316
379
  stream_id
@@ -318,6 +381,9 @@ module Cassandra
318
381
  nil
319
382
  end
320
383
  end
384
+
385
+ HEARTBEAT = OptionsRequest.new
386
+ TERMINATED = Errors::TimeoutError.new('Terminated due to inactivity')
321
387
  end
322
388
  end
323
389
  end
@@ -47,7 +47,7 @@ module Cassandra
47
47
 
48
48
  def actual_decode(buffer, fields, size)
49
49
  if (fields >> 24) & 0x80 == 0
50
- raise UnsupportedFrameTypeError, 'Request frames are not supported'
50
+ raise Errors::DecodingError, 'Request frames are not supported'
51
51
  end
52
52
  protocol_version = (fields >> 24) & 0x7f
53
53
  compression = (fields >> 16) & 0x01
@@ -78,7 +78,7 @@ module Cassandra
78
78
  compressed_body = buffer.read(size)
79
79
  CqlByteBuffer.new(@compressor.decompress(compressed_body))
80
80
  else
81
- raise UnexpectedCompressionError, 'Compressed frame received, but no compressor configured'
81
+ raise Errors::DecodingError, 'Compressed frame received, but no compressor configured'
82
82
  end
83
83
  end
84
84
 
@@ -26,7 +26,7 @@ module Cassandra
26
26
  end
27
27
 
28
28
  def encode_frame(request, stream_id=0, buffer=nil)
29
- raise InvalidStreamIdError, 'The stream ID must be between 0 and 127' unless 0 <= stream_id && stream_id < 128
29
+ raise EncodingError, 'The stream ID must be between 0 and 127' unless 0 <= stream_id && stream_id < 128
30
30
  buffer ||= CqlByteBuffer.new
31
31
  flags = request.trace? ? 2 : 0
32
32
  body = request.write(@protocol_version, CqlByteBuffer.new)
@@ -24,7 +24,7 @@ module Cassandra
24
24
  if response_class
25
25
  response_class.decode(protocol_version, buffer, length, trace_id)
26
26
  else
27
- raise UnsupportedOperationError, "The operation #{opcode} is not supported"
27
+ raise Errors::DecodingError, "Unsupported opcode #{opcode.inspect}"
28
28
  end
29
29
  end
30
30
 
@@ -37,7 +37,10 @@ module Cassandra
37
37
  details[:cl] = buffer.read_consistency
38
38
  details[:received] = buffer.read_int
39
39
  details[:blockfor] = buffer.read_int
40
- details[:write_type] = buffer.read_string
40
+ write_type = buffer.read_string
41
+ write_type.downcase!
42
+
43
+ details[:write_type] = write_type.to_sym
41
44
  when 0x1200 # read_timeout
42
45
  details[:cl] = buffer.read_consistency
43
46
  details[:received] = buffer.read_int
@@ -52,6 +55,18 @@ module Cassandra
52
55
  new(code, message, details)
53
56
  end
54
57
 
58
+ def to_error(statement = nil)
59
+ case code
60
+ when 0x1000 then Errors::UnavailableError.new(@message, statement, @details[:cl], @details[:required], @details[:alive])
61
+ when 0x1100 then Errors::WriteTimeoutError.new(@message, statement, @details[:write_type], @details[:cl], @details[:blockfor], @details[:received])
62
+ when 0x1200 then Errors::ReadTimeoutError.new(@message, statement, @details[:data_present], @details[:cl], @details[:blockfor], @details[:received])
63
+ when 0x2400 then Errors::AlreadyExistsError.new(@message, statement, @details[:ks], @details[:table])
64
+ when 0x2500 then Errors::UnpreparedError.new(@message, statement, @details[:id])
65
+ else
66
+ super
67
+ end
68
+ end
69
+
55
70
  def to_s
56
71
  "#{super} #{@details}"
57
72
  end
@@ -42,6 +42,23 @@ module Cassandra
42
42
  %(ERROR 0x#{hex_code} "#@message")
43
43
  end
44
44
 
45
+ def to_error(statement = nil)
46
+ case @code
47
+ when 0x0000 then Errors::ServerError.new(@message)
48
+ when 0x000A then Errors::ProtocolError.new(@message)
49
+ when 0x0100 then Errors::AuthenticationError.new(@message)
50
+ when 0x1001 then Errors::OverloadedError.new(@message, statement)
51
+ when 0x1002 then Errors::IsBootstrappingError.new(@message, statement)
52
+ when 0x1003 then Errors::TruncateError.new(@message, statement)
53
+ when 0x2000 then Errors::SyntaxError.new(@message, statement)
54
+ when 0x2100 then Errors::UnauthorizedError.new(@message, statement)
55
+ when 0x2200 then Errors::InvalidError.new(@message, statement)
56
+ when 0x2300 then Errors::ConfigurationError.new(@message, statement)
57
+ else
58
+ Errors::ServerError.new(@message)
59
+ end
60
+ end
61
+
45
62
  private
46
63
 
47
64
  RESPONSE_TYPES[0x00] = self