Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions lib/gooddata/models/user_filters/user_filter_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ module UserFilterBuilder
@all_domain_users = {}
@mutex = Mutex.new

# Caps on error entries embedded in a raised exception's message (and logged).
# Domain-wide syncs can produce one entry per user/value across a whole domain,
# and a single entry can embed large value lists; stringifying the raw
# collection exhausts the JRuby heap (GRIF-951).
MAX_ERRORS_IN_MESSAGE = 10
MAX_ERROR_ENTRY_CHARS = 1_000

# Renders a bounded sample of an error collection for logs and exception
# messages: caps both the number of entries and each entry's rendered size.
def self.bounded_error_sample(errors)
errors.take(MAX_ERRORS_IN_MESSAGE).map { |e| e.inspect.slice(0, MAX_ERROR_ENTRY_CHARS) }
Comment thread
coderabbitai[bot] marked this conversation as resolved.
end

# Main Entry function. Gets values and processes them to get filters
# that are suitable for other function to process.
# Values can be read from file or provided inline as an array.
Expand Down Expand Up @@ -488,7 +501,9 @@ def self.execute_mufs(user_filters, options = {})
errors = errors.map do |e|
e.merge(pid: project.pid)
end
fail GoodData::FilterMaqlizationError, errors
sample = bounded_error_sample(errors)
GoodData.logger.error("Maqlizing MUFs failed with #{errors.size} error(s). First #{sample.size}: #{sample.pretty_inspect}")
fail GoodData::FilterMaqlizationError, "Maqlizing MUFs resulted in #{errors.size} error(s). First #{sample.size}: #{sample}"
end

filters = user_filters.map { |data| client.create(MandatoryUserFilter, data, project: project) }
Expand Down Expand Up @@ -542,7 +557,11 @@ def self.execute_mufs(user_filters, options = {})
end
project_log_formatter.log_user_filter_results(create_results, to_create)
create_errors = create_results.select { |r| r[:status] == :failed }
fail "Creating MUFs resulted in errors: #{create_errors}" if create_errors.any?
if create_errors.any?
sample = bounded_error_sample(create_errors)
GoodData.logger.error("Creating MUFs failed with #{create_errors.size} error(s). First #{sample.size}: #{sample.pretty_inspect}")
fail "Creating MUFs resulted in errors, count: #{create_errors.size}, first #{sample.size}: #{sample}"
end
end

if to_delete.empty?
Expand Down Expand Up @@ -575,7 +594,11 @@ def self.execute_mufs(user_filters, options = {})

project_log_formatter.log_user_filter_results(delete_results, to_delete)
delete_errors = delete_results.select { |r| r[:status] == :failed } if delete_results
fail "Deleting MUFs resulted in errors: #{delete_errors}" if delete_errors&.any?
if delete_errors&.any?
sample = bounded_error_sample(delete_errors)
GoodData.logger.error("Deleting MUFs failed with #{delete_errors.size} error(s). First #{sample.size}: #{sample.pretty_inspect}")
fail "Deleting MUFs resulted in errors, count: #{delete_errors.size}, first #{sample.size}: #{sample}"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
end
end
end

Expand Down Expand Up @@ -643,7 +666,11 @@ def self.execute(user_filters, project_filters, klass, options = {})
maqlify_filters(filters, users, options.merge(users_cache: users_cache, users_must_exist: users_must_exist))
end

fail GoodData::FilterMaqlizationError, errors if !ignore_missing_values && !errors.empty?
if !ignore_missing_values && !errors.empty?
sample = bounded_error_sample(errors)
GoodData.logger.error("Maqlizing variables failed with #{errors.size} error(s). First #{sample.size}: #{sample.pretty_inspect}")
fail GoodData::FilterMaqlizationError, "Maqlizing variables resulted in #{errors.size} error(s). First #{sample.size}: #{sample}"
end
filters = user_filters.map { |data| client.create(klass, data, project: project) }
resolve_user_filters(filters, project_filters)
end
Expand Down
21 changes: 21 additions & 0 deletions spec/unit/models/user_filters/user_filter_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -199,5 +199,26 @@
expect { subject.execute_mufs(filter_definitions, options) }.to raise_error(/Creating MUFs resulted in errors/)
end
end

context 'when creating MUFs results in a large number of errors' do
let(:failed_users) do
Array.new(500) { |i| { 'login' => "user#{i}@example.com", 'detail' => 'x' * 1_000 } }
end

before do
allow(client).to receive(:post)
.and_return 'userFiltersUpdateResult' => { 'failed' => failed_users }
end

it 'raises an error with a message bounded to the first few errors' do
expect { subject.execute_mufs(filter_definitions, options) }.to raise_error do |error|
expect(error.message).to match(/Creating MUFs resulted in errors, count: 500, first 10/)
expect(error.message).to include('user0@example.com')
expect(error.message).to include('user9@example.com')
expect(error.message).not_to include('user10@example.com')
expect(error.message.size).to be < 15_000
Comment thread
coderabbitai[bot] marked this conversation as resolved.
end
end
end
end
end
Loading