avro-registered-schema-decoder 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d490a609d6c3b2d0d1ecceba3019d1392021b7c6
4
+ data.tar.gz: 6e10f76246cabc413f8a1e602bf0f10ae0e9d1e2
5
+ SHA512:
6
+ metadata.gz: c87c293bd4b20094cf66105e0cf09db28bae70e8c0609f66ebc3025d7058ab25d186887f05928e36a2add66c38bbf322c5e467bf153edaeb04d9abf78930489c
7
+ data.tar.gz: 3918b0ed6311532def01d253db08c71c4a5e3921a735bf5cd9fc704d3210adb56769bb640df618d1274133cc81e98de8438b6764b8e3049ca3a4aff1306efa2b
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'avro', 'registered_schema_decoder')
@@ -0,0 +1,97 @@
1
+ require 'avro'
2
+ require 'schema_registry'
3
+
4
+ module Avro
5
+ class RegisteredSchemaDecoder
6
+ class Error < StandardError
7
+ attr_reader :cause
8
+ def initialize(message, cause = $!)
9
+ super(message)
10
+ @cause = cause
11
+ end
12
+
13
+ def to_s
14
+ "#{super}#{" (caused by: #{cause})" if cause}"
15
+ end
16
+ end
17
+
18
+ def initialize(registry_url, logger: nil)
19
+ @registry = SchemaRegistry::Client.new(registry_url)
20
+ @logger = logger
21
+ @readers = {}
22
+ end
23
+
24
+ def decode_key(key_bytes)
25
+ _, key = decode_key_with_schema(key_bytes)
26
+ key
27
+ end
28
+
29
+ def decode_key_with_schema(key_bytes)
30
+ # expected for unkeyed messages
31
+ return if key_bytes.nil?
32
+
33
+ decode_message(key_bytes)
34
+ end
35
+
36
+ def decode_value(value_bytes)
37
+ _, value = decode_value_with_schema(value_bytes)
38
+ value
39
+ end
40
+
41
+ def decode_value_with_schema(value_bytes)
42
+ # expected, used to signify record deletion
43
+ return if value_bytes.nil?
44
+ # handle producers using older versions of librdkafka, which sent zero-
45
+ # length messages instead of null messages:
46
+ # see https://github.com/confluentinc/bottledwater-pg/pull/33
47
+ return if value_bytes.empty?
48
+
49
+ decode_message(value_bytes)
50
+ end
51
+
52
+ private
53
+ # message format:
54
+ #
55
+ # Every message sent to Kafka is Avro-encoded, and prefixed with five
56
+ # bytes:
57
+ # - The first byte is always 0, and reserved for future use.
58
+ # - The next four bytes are the schema ID in big-endian byte order.
59
+ #
60
+ # per https://github.com/confluentinc/bottledwater-pg/blob/8a11825/kafka/registry.c#L6-L8
61
+ def decode_message(bytes)
62
+ raise "Empty message" if bytes.empty?
63
+ reserved, schema_id, avro = bytes.unpack('cNa*')
64
+ raise "Reserved byte #{reserved.inspect} in message header (expected 0)!\nmessage: #{bytes.inspect}" unless 0 == reserved
65
+
66
+ reader = reader_for(schema_id)
67
+ decoder = Avro::IO::BinaryDecoder.new(StringIO.new(avro))
68
+
69
+ wrap_error "parsing message with schema #{schema_id}" do
70
+ [reader.writers_schema, reader.read(decoder)]
71
+ end
72
+ end
73
+
74
+ def reader_for(schema_id)
75
+ if @readers.key?(schema_id)
76
+ return @readers[schema_id]
77
+ end
78
+
79
+ @logger.debug("Fetching schema #{schema_id}") if @logger
80
+ schema = nil
81
+ wrap_error "fetching schema #{schema_id}" do
82
+ schema_json = @registry.schema(schema_id)
83
+ schema = Avro::Schema.parse(schema_json)
84
+ end
85
+
86
+ Avro::IO::DatumReader.new(schema).tap do |reader|
87
+ @readers[schema_id] = reader
88
+ end
89
+ end
90
+
91
+ def wrap_error(doing_what)
92
+ yield
93
+ rescue
94
+ raise Error, "error #{doing_what}"
95
+ end
96
+ end
97
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: avro-registered-schema-decoder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sam Stokes
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: avro
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: schema_registry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: |
84
+ Provides a decoder for messages encoded as "prefixed Avro", where the binary
85
+ Avro encoding is prefixed with a schema id referring to a schema stored in a
86
+ Confluent Schema Registry
87
+ (http://docs.confluent.io/1.0/schema-registry/docs/intro.html).
88
+ email:
89
+ - me@samstokes.co.uk
90
+ executables: []
91
+ extensions: []
92
+ extra_rdoc_files: []
93
+ files:
94
+ - lib/avro-registered-schema-decoder.rb
95
+ - lib/avro/registered_schema_decoder.rb
96
+ homepage: https://github.com/samstokes/avro-registered-schema-decoder
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.4.5.1
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: Decodes messages encoded as prefixed Avro, looking up schemas in a schema
120
+ registry
121
+ test_files: []