fluentd-plugin-kinesis-intuit 2.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.
- checksums.yaml +7 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/aws-fluentd-plugin-kinesis-intuit.iml +55 -0
- data/.idea/misc.xml +7 -0
- data/.idea/modules.xml +8 -0
- data/.idea/vcs.xml +6 -0
- data/.idea/workspace.xml +1029 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.md +115 -0
- data/CODE_OF_CONDUCT.md +4 -0
- data/COMMUNITY.md +26 -0
- data/CONTRIBUTING.md +160 -0
- data/CONTRIBUTORS.txt +6 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +112 -0
- data/LICENSE.txt +201 -0
- data/Makefile +44 -0
- data/NOTICE.txt +2 -0
- data/README.md +62 -0
- data/Rakefile +26 -0
- data/benchmark/task.rake +106 -0
- data/fluent-plugin-kinesis.gemspec +53 -0
- data/gemfiles/Gemfile.fluentd-0.14.10 +20 -0
- data/gemfiles/Gemfile.td-agent-3.2.0 +31 -0
- data/lib/fluent/plugin/kinesis.rb +146 -0
- data/lib/fluent/plugin/kinesis_helper/aggregator.rb +101 -0
- data/lib/fluent/plugin/kinesis_helper/api.rb +198 -0
- data/lib/fluent/plugin/kinesis_helper/client.rb +170 -0
- data/lib/fluent/plugin/out_kinesis_firehose.rb +59 -0
- data/lib/fluent/plugin/out_kinesis_streams.rb +160 -0
- data/lib/fluent/plugin/out_kinesis_streams_aggregated.rb +78 -0
- data/lib/fluent_plugin_kinesis/version.rb +17 -0
- metadata +290 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
5
|
+
# may not use this file except in compliance with the License. A copy of
|
6
|
+
# the License is located at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is
|
11
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
12
|
+
# ANY KIND, either express or implied. See the License for the specific
|
13
|
+
# language governing permissions and limitations under the License.
|
14
|
+
|
15
|
+
require 'fluent/configurable'
|
16
|
+
require 'aws-sdk-core'
|
17
|
+
|
18
|
+
module Fluent
|
19
|
+
module Plugin
|
20
|
+
module KinesisHelper
|
21
|
+
module Client
|
22
|
+
module ClientParams
|
23
|
+
include Fluent::Configurable
|
24
|
+
config_param :region, :string, default: nil
|
25
|
+
|
26
|
+
config_param :http_proxy, :string, default: nil, secret: true
|
27
|
+
config_param :endpoint, :string, default: nil
|
28
|
+
config_param :ssl_verify_peer, :bool, default: true
|
29
|
+
|
30
|
+
config_param :aws_key_id, :string, default: nil, secret: true
|
31
|
+
config_param :aws_sec_key, :string, default: nil, secret: true
|
32
|
+
config_section :assume_role_credentials, multi: false do
|
33
|
+
desc "The Amazon Resource Name (ARN) of the role to assume"
|
34
|
+
config_param :role_arn, :string, secret: true
|
35
|
+
desc "An identifier for the assumed role session"
|
36
|
+
config_param :role_session_name, :string
|
37
|
+
desc "An IAM policy in JSON format"
|
38
|
+
config_param :policy, :string, default: nil
|
39
|
+
desc "The duration, in seconds, of the role session (900-3600)"
|
40
|
+
config_param :duration_seconds, :integer, default: nil
|
41
|
+
desc "A unique identifier that is used by third parties when assuming roles in their customers' accounts."
|
42
|
+
config_param :external_id, :string, default: nil, secret: true
|
43
|
+
desc "A http proxy url for requests to aws sts service"
|
44
|
+
config_param :sts_http_proxy, :string, default: nil, secret: true
|
45
|
+
end
|
46
|
+
config_section :instance_profile_credentials, multi: false do
|
47
|
+
desc "Number of times to retry when retrieving credentials"
|
48
|
+
config_param :retries, :integer, default: nil
|
49
|
+
desc "IP address (default:169.254.169.254)"
|
50
|
+
config_param :ip_address, :string, default: nil
|
51
|
+
desc "Port number (default:80)"
|
52
|
+
config_param :port, :integer, default: nil
|
53
|
+
desc "Number of seconds to wait for the connection to open"
|
54
|
+
config_param :http_open_timeout, :float, default: nil
|
55
|
+
desc "Number of seconds to wait for one block to be read"
|
56
|
+
config_param :http_read_timeout, :float, default: nil
|
57
|
+
# config_param :delay, :integer or :proc, :default => nil
|
58
|
+
# config_param :http_degub_output, :io, :default => nil
|
59
|
+
end
|
60
|
+
config_section :shared_credentials, multi: false do
|
61
|
+
desc "Path to the shared file. (default: $HOME/.aws/credentials)"
|
62
|
+
config_param :path, :string, default: nil
|
63
|
+
desc "Profile name. Default to 'default' or ENV['AWS_PROFILE']"
|
64
|
+
config_param :profile_name, :string, default: nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.included(mod)
|
69
|
+
mod.include ClientParams
|
70
|
+
end
|
71
|
+
|
72
|
+
def configure(conf)
|
73
|
+
super
|
74
|
+
@region = client.config.region if @region.nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
def client
|
78
|
+
@client ||= client_class.new(client_options)
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def aws_sdk_v2?
|
84
|
+
@aws_sdk_v2 ||= Gem.loaded_specs['aws-sdk-core'].version < Gem::Version.create('3')
|
85
|
+
end
|
86
|
+
|
87
|
+
def client_class
|
88
|
+
case request_type
|
89
|
+
when :streams, :streams_aggregated
|
90
|
+
if aws_sdk_v2?
|
91
|
+
require 'aws-sdk'
|
92
|
+
else
|
93
|
+
require 'aws-sdk-kinesis'
|
94
|
+
end
|
95
|
+
Aws::Kinesis::Client
|
96
|
+
when :firehose
|
97
|
+
if aws_sdk_v2?
|
98
|
+
require 'aws-sdk'
|
99
|
+
else
|
100
|
+
require 'aws-sdk-firehose'
|
101
|
+
end
|
102
|
+
Aws::Firehose::Client
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def client_options
|
107
|
+
options = setup_credentials
|
108
|
+
options.update(
|
109
|
+
user_agent_suffix: "fluent-plugin-kinesis/#{request_type}/#{FluentPluginKinesis::VERSION}"
|
110
|
+
)
|
111
|
+
options.update(region: @region) unless @region.nil?
|
112
|
+
options.update(http_proxy: @http_proxy) unless @http_proxy.nil?
|
113
|
+
options.update(endpoint: @endpoint) unless @endpoint.nil?
|
114
|
+
options.update(ssl_verify_peer: @ssl_verify_peer) unless @ssl_verify_peer.nil?
|
115
|
+
if @debug
|
116
|
+
options.update(logger: Logger.new(log.out))
|
117
|
+
options.update(log_level: :debug)
|
118
|
+
end
|
119
|
+
options
|
120
|
+
end
|
121
|
+
|
122
|
+
def setup_credentials
|
123
|
+
options = {}
|
124
|
+
credentials_options = {}
|
125
|
+
case
|
126
|
+
when @aws_key_id && @aws_sec_key
|
127
|
+
options[:access_key_id] = @aws_key_id
|
128
|
+
options[:secret_access_key] = @aws_sec_key
|
129
|
+
when @assume_role_credentials
|
130
|
+
c = @assume_role_credentials
|
131
|
+
credentials_options[:role_arn] = c.role_arn
|
132
|
+
credentials_options[:role_session_name] = c.role_session_name
|
133
|
+
credentials_options[:policy] = c.policy if c.policy
|
134
|
+
credentials_options[:duration_seconds] = c.duration_seconds if c.duration_seconds
|
135
|
+
credentials_options[:external_id] = c.external_id if c.external_id
|
136
|
+
if c.sts_http_proxy and @region
|
137
|
+
credentials_options[:client] = Aws::STS::Client.new(region: @region, http_proxy: c.sts_http_proxy)
|
138
|
+
elsif @region
|
139
|
+
credentials_options[:client] = Aws::STS::Client.new(region: @region)
|
140
|
+
elsif c.sts_http_proxy
|
141
|
+
credentials_options[:client] = Aws::STS::Client.new(http_proxy: c.sts_http_proxy)
|
142
|
+
end
|
143
|
+
options[:credentials] = Aws::AssumeRoleCredentials.new(credentials_options)
|
144
|
+
when @instance_profile_credentials
|
145
|
+
c = @instance_profile_credentials
|
146
|
+
credentials_options[:retries] = c.retries if c.retries
|
147
|
+
credentials_options[:ip_address] = c.ip_address if c.ip_address
|
148
|
+
credentials_options[:port] = c.port if c.port
|
149
|
+
credentials_options[:http_open_timeout] = c.http_open_timeout if c.http_open_timeout
|
150
|
+
credentials_options[:http_read_timeout] = c.http_read_timeout if c.http_read_timeout
|
151
|
+
if ENV["AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"]
|
152
|
+
options[:credentials] = Aws::ECSCredentials.new(credentials_options)
|
153
|
+
else
|
154
|
+
options[:credentials] = Aws::InstanceProfileCredentials.new(credentials_options)
|
155
|
+
end
|
156
|
+
when @shared_credentials
|
157
|
+
c = @shared_credentials
|
158
|
+
credentials_options[:path] = c.path if c.path
|
159
|
+
credentials_options[:profile_name] = c.profile_name if c.profile_name
|
160
|
+
options[:credentials] = Aws::SharedCredentials.new(credentials_options)
|
161
|
+
else
|
162
|
+
# Use default credentials
|
163
|
+
# See http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Client.html
|
164
|
+
end
|
165
|
+
options
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
5
|
+
# may not use this file except in compliance with the License. A copy of
|
6
|
+
# the License is located at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is
|
11
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
12
|
+
# ANY KIND, either express or implied. See the License for the specific
|
13
|
+
# language governing permissions and limitations under the License.
|
14
|
+
|
15
|
+
require 'fluent/plugin/kinesis'
|
16
|
+
|
17
|
+
module Fluent
|
18
|
+
module Plugin
|
19
|
+
class KinesisFirehoseOutput < KinesisOutput
|
20
|
+
Fluent::Plugin.register_output('kinesis_firehose', self)
|
21
|
+
|
22
|
+
RequestType = :firehose
|
23
|
+
BatchRequestLimitCount = 500
|
24
|
+
BatchRequestLimitSize = 4 * 1024 * 1024
|
25
|
+
include KinesisHelper::API::BatchRequest
|
26
|
+
|
27
|
+
config_param :delivery_stream_name, :string
|
28
|
+
config_param :append_new_line, :bool, default: true
|
29
|
+
|
30
|
+
def configure(conf)
|
31
|
+
super
|
32
|
+
if @append_new_line
|
33
|
+
org_data_formatter = @data_formatter
|
34
|
+
@data_formatter = ->(tag, time, record) {
|
35
|
+
org_data_formatter.call(tag, time, record) + "\n"
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def format(tag, time, record)
|
41
|
+
format_for_api do
|
42
|
+
[@data_formatter.call(tag, time, record)]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def write(chunk)
|
47
|
+
write_records_batch(chunk) do |batch|
|
48
|
+
records = batch.map{|(data)|
|
49
|
+
{ data: data }
|
50
|
+
}
|
51
|
+
client.put_record_batch(
|
52
|
+
delivery_stream_name: @delivery_stream_name,
|
53
|
+
records: records,
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
5
|
+
# may not use this file except in compliance with the License. A copy of
|
6
|
+
# the License is located at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is
|
11
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
12
|
+
# ANY KIND, either express or implied. See the License for the specific
|
13
|
+
# language governing permissions and limitations under the License.
|
14
|
+
|
15
|
+
require 'fluent/plugin/kinesis'
|
16
|
+
|
17
|
+
module Fluent
|
18
|
+
module Plugin
|
19
|
+
class KinesisStreamsOutput < KinesisOutput
|
20
|
+
Fluent::Plugin.register_output('kinesis_streams', self)
|
21
|
+
|
22
|
+
RequestType = :streams
|
23
|
+
BatchRequestLimitCount = 500
|
24
|
+
BatchRequestLimitSize = 5 * 1024 * 1024
|
25
|
+
include KinesisHelper::API::BatchRequest
|
26
|
+
|
27
|
+
config_param :stream_name, :string
|
28
|
+
config_param :partition_key, :string, default: nil
|
29
|
+
|
30
|
+
desc 'The Splunk index to index events. When not set, will be decided by HEC'
|
31
|
+
config_param :index, :string, default: nil
|
32
|
+
|
33
|
+
desc 'The host field for events, by default it uses the hostname of the machine that runnning fluentd. '
|
34
|
+
config_param :host, :string, default: nil
|
35
|
+
|
36
|
+
desc 'The source field for events, when not set, will be decided by HEC.'
|
37
|
+
config_param :source, :string, default: nil
|
38
|
+
|
39
|
+
desc 'The sourcetype field for events, when not set, will be decided by HEC.'
|
40
|
+
config_param :sourcetype, :string, default: nil
|
41
|
+
|
42
|
+
desc 'All items in the record will be sent if true, otherwise only message'
|
43
|
+
config_param :all_items, :bool, :default => false
|
44
|
+
|
45
|
+
def initialize
|
46
|
+
super
|
47
|
+
@default_host = Socket.gethostname
|
48
|
+
end
|
49
|
+
|
50
|
+
# Thanks to
|
51
|
+
# https://github.com/kazegusuri/fluent-plugin-prometheus/blob/348c112d/lib/fluent/plugin/prometheus.rb
|
52
|
+
def self.placeholder_expander(log)
|
53
|
+
# Use internal class in order to expand placeholder
|
54
|
+
if defined?(Fluent::Filter) # for v0.12, built-in PlaceholderExpander
|
55
|
+
begin
|
56
|
+
require 'fluent/plugin/filter_record_transformer'
|
57
|
+
if defined?(Fluent::Plugin::RecordTransformerFilter::PlaceholderExpander)
|
58
|
+
# for v0.14
|
59
|
+
return Fluent::Plugin::RecordTransformerFilter::PlaceholderExpander.new(log: log)
|
60
|
+
else
|
61
|
+
# for v0.12
|
62
|
+
return Fluent::RecordTransformerFilter::PlaceholderExpander.new(log: log)
|
63
|
+
end
|
64
|
+
rescue LoadError => e
|
65
|
+
raise ConfigError, "cannot find filter_record_transformer plugin: #{e.message}"
|
66
|
+
end
|
67
|
+
else # for v0.10, use PlaceholderExapander in fluent-plugin-record-reformer plugin
|
68
|
+
begin
|
69
|
+
require 'fluent/plugin/out_record_reformer.rb'
|
70
|
+
return Fluent::RecordReformerOutput::PlaceholderExpander.new(log: log)
|
71
|
+
rescue LoadError => e
|
72
|
+
raise ConfigError, "cannot find fluent-plugin-record-reformer: #{e.message}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def configure(conf)
|
78
|
+
super
|
79
|
+
# check_conflict
|
80
|
+
# prepare_key_fields
|
81
|
+
@placeholder_expander = Fluent::Plugin::KinesisStreamsOutput.placeholder_expander(log)
|
82
|
+
@hostname = Socket.gethostname
|
83
|
+
@key_formatter = key_formatter_create
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def format(tag, time, record)
|
88
|
+
|
89
|
+
placeholder_values = {
|
90
|
+
'tag' => tag,
|
91
|
+
'tag_parts' => tag.split('.'),
|
92
|
+
'hostname' => @default_host,
|
93
|
+
'record' => record
|
94
|
+
}
|
95
|
+
|
96
|
+
placeholders = @placeholder_expander.prepare_placeholders(placeholder_values)
|
97
|
+
|
98
|
+
payload = {
|
99
|
+
# for v0.14 millisecs time precision
|
100
|
+
time: time.is_a?(Integer) ? time.to_i : time.to_f,
|
101
|
+
source: @source.nil? ? tag.to_s : @placeholder_expander.expand(@source, placeholders),
|
102
|
+
sourcetype: @placeholder_expander.expand(@sourcetype.to_s, placeholders),
|
103
|
+
host: @host.nil? ? @default_host : @placeholder_expander.expand(@host.to_s, placeholders),
|
104
|
+
index: @placeholder_expander.expand(@index.to_s, placeholders)
|
105
|
+
}
|
106
|
+
#payload[:index] = @index if @index
|
107
|
+
#payload[:source] = @source if @source
|
108
|
+
#payload[:sourcetype] = @sourcetype if @sourcetype
|
109
|
+
|
110
|
+
# delete nil fields otherwise will get formet error from HEC
|
111
|
+
%i[host index source sourcetype].each { |f| payload.delete f if payload[f].nil? }
|
112
|
+
|
113
|
+
if !record[@data_key].nil?
|
114
|
+
payload[@data_key] = record[@data_key]
|
115
|
+
end
|
116
|
+
|
117
|
+
if @all_items
|
118
|
+
payload[:event] = record
|
119
|
+
else
|
120
|
+
payload[:event] = record["message"]
|
121
|
+
end
|
122
|
+
|
123
|
+
format_for_api do
|
124
|
+
data = @data_formatter.call(tag, time, payload)
|
125
|
+
key = @key_formatter.call(payload)
|
126
|
+
[data, key]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def write(chunk)
|
131
|
+
write_records_batch(chunk) do |batch|
|
132
|
+
records = batch.map{|(data, partition_key)|
|
133
|
+
{ data: data, partition_key: partition_key }
|
134
|
+
}
|
135
|
+
client.put_records(
|
136
|
+
stream_name: @stream_name,
|
137
|
+
records: records,
|
138
|
+
)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def key_formatter_create
|
145
|
+
if @partition_key.nil?
|
146
|
+
->(record) { SecureRandom.hex(16) }
|
147
|
+
else
|
148
|
+
->(record) {
|
149
|
+
if !record.key?(@partition_key)
|
150
|
+
raise KeyNotFoundError.new(@partition_key, record)
|
151
|
+
end
|
152
|
+
record[@partition_key]
|
153
|
+
}
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
5
|
+
# may not use this file except in compliance with the License. A copy of
|
6
|
+
# the License is located at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is
|
11
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
12
|
+
# ANY KIND, either express or implied. See the License for the specific
|
13
|
+
# language governing permissions and limitations under the License.
|
14
|
+
|
15
|
+
require 'fluent/plugin/kinesis'
|
16
|
+
require 'fluent/plugin/kinesis_helper/aggregator'
|
17
|
+
|
18
|
+
module Fluent
|
19
|
+
module Plugin
|
20
|
+
class KinesisStreamsAggregatedOutput < KinesisOutput
|
21
|
+
Fluent::Plugin.register_output('kinesis_streams_aggregated', self)
|
22
|
+
include KinesisHelper::Aggregator::Mixin
|
23
|
+
|
24
|
+
RequestType = :streams_aggregated
|
25
|
+
BatchRequestLimitCount = 100_000
|
26
|
+
BatchRequestLimitSize = 1024 * 1024
|
27
|
+
include KinesisHelper::API::BatchRequest
|
28
|
+
|
29
|
+
config_param :stream_name, :string
|
30
|
+
config_param :fixed_partition_key, :string, default: nil
|
31
|
+
|
32
|
+
def configure(conf)
|
33
|
+
super
|
34
|
+
@partition_key_generator = create_partition_key_generator
|
35
|
+
@batch_request_max_size -= offset
|
36
|
+
@max_record_size -= offset
|
37
|
+
end
|
38
|
+
|
39
|
+
def format(tag, time, record)
|
40
|
+
format_for_api do
|
41
|
+
[@data_formatter.call(tag, time, record)]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def write(chunk)
|
46
|
+
write_records_batch(chunk) do |batch|
|
47
|
+
key = @partition_key_generator.call
|
48
|
+
records = batch.map{|(data)|data}
|
49
|
+
client.put_records(
|
50
|
+
stream_name: @stream_name,
|
51
|
+
records: [{
|
52
|
+
partition_key: key,
|
53
|
+
data: aggregator.aggregate(records, key),
|
54
|
+
}],
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def offset
|
60
|
+
@offset ||= AggregateOffset + @partition_key_generator.call.size*2
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def size_of_values(record)
|
66
|
+
super(record) + RecordOffset
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_partition_key_generator
|
70
|
+
if @fixed_partition_key.nil?
|
71
|
+
->() { SecureRandom.hex(16) }
|
72
|
+
else
|
73
|
+
->() { @fixed_partition_key }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|