Skip to content

Conversation

@rashidsp
Copy link
Contributor

@rashidsp rashidsp commented Aug 1, 2019

Summary

  • Introduces an EventProcessor interface.
  • Introduces a BatchEventProcessor
  • Excluded batch_event_processor.rb from rubocop's Metrics/ParameterLists rule.

Buffering events within a queue before dispatching is an optimization that should prevent SDK implementations from exhausting resources while increasing throughput. This implementation relies on a EventQueue to buffer events received from one-to-many producers. A single consumer thread continuously polls from this queue to build a batch before emitting the batched LogEvent.

Test Plan

  • Added unit tests

@rashidsp rashidsp added the WIP Work in progress label Aug 1, 2019
@rashidsp rashidsp changed the title feat(eventProcessor): Adds EventProcessor and BatchEventProcessor- Do Not Review feat(eventProcessor): BatchEventProcessor- Do Not Review Aug 1, 2019
@rashidsp rashidsp requested a review from mnoman09 August 1, 2019 12:07
@coveralls
Copy link

coveralls commented Aug 1, 2019

Coverage Status

Coverage decreased (-0.09%) to 99.385% when pulling 184fd06 on rashid/batch-event-processor into 7accf32 on master.

Copy link
Contributor

@mnoman09 mnoman09 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, Just added few comments.


DEFAULT_BATCH_SIZE = 10
DEFAULT_FLUSH_INTERVAL = 30_000
DEFAULT_TIMEOUT_INTERVAL = (5000 * 60)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this variable as it is of no use.

event_dispatcher:,
batch_size:,
flush_interval:,
timeout_interval:,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this as you are just passing it and it is not getting used anywhere.

@received.wait(@mutex, 0.05)
end

item = @event_queue.pop
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be in mutex.synchronize block?

def flush_queue!
return if @current_batch.empty?

log_event = Optimizely::EventFactory.create_log_event(@current_batch, @logger)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think,

  1. We should put current_batch in temp_queue.
  2. Clear current_batch
  3. Pass temp_queue in create_log_event
    All this should happen inside synchornized block.
    See Csharp for reference.

@msohailhussain what do you suggest?

# Reset the deadline if starting a new batch.
@flushing_interval_deadline = Helpers::DateTimeUtils.create_timestamp + @flush_interval if @current_batch.empty?

@logger.log(Logger::DEBUG, "Adding user event: #{user_event.event['key']} to btach.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo:

Suggested change
@logger.log(Logger::DEBUG, "Adding user event: #{user_event.event['key']} to btach.")
@logger.log(Logger::DEBUG, "Adding user event: #{user_event.event['key']} to batch.")

@flushing_interval_deadline = Helpers::DateTimeUtils.create_timestamp + @flush_interval if @current_batch.empty?

@logger.log(Logger::DEBUG, "Adding user event: #{user_event.event['key']} to btach.")
@current_batch << user_event
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also add mutex lock here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We add mutex lock, when we add event into EventQueue!

class EventProcessor
# EventProcessor interface is used to provide an intermediary processing stage within
# event production. It's assumed that the EventProcessor dispatches events via a provided
# EventHandler.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: change it to EventDispatcher.

allow(@event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event))
end

it 'return empty event queue event is processed' do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also make sure if the dispatchEvent is getting called and logevent is correct?

@rashidsp rashidsp force-pushed the rashid/batch-event-processor branch from c7c2552 to 28ea204 Compare August 2, 2019 07:53
@rashidsp rashidsp requested a review from mnoman09 August 2, 2019 16:28
Copy link
Contributor

@msohailhussain msohailhussain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please address comments

- 'lib/optimizely/event/entity/snapshot_event.rb'
- 'lib/optimizely/event/entity/conversion_event.rb'
- 'lib/optimizely/event/entity/event_context.rb'
- 'lib/optimizely/event/batch_event_processor.rb'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alphabetize order

event_dispatcher:,
batch_size:,
flush_interval:,
start_by_default: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think so it's really needed.

@event_dispatcher = event_dispatcher
@batch_size = batch_size || DEFAULT_BATCH_SIZE
@flush_interval = flush_interval || DEFAULT_BATCH_INTERVAL
@logger = logger
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it assign NoOpLogger if its nil

@mutex = Mutex.new
@received = ConditionVariable.new
@current_batch = []
@disposed = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disposed is it also implemented in http_project_config_manager?

return
end

if @event_queue.include? user_event
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what this condition mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated for DEFAULT_QUEUE_CAPACITY

@current_batch = []
end

# Reset the deadline if starting a new batch.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this condition can't b in the if

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline condition; Rubocop's auto-fix

@rashidsp rashidsp changed the title feat(eventProcessor): BatchEventProcessor- Do Not Review feat(eventProcessor): Adds EventProcessor and BatchEventProcessor Aug 5, 2019
@rashidsp rashidsp removed the WIP Work in progress label Aug 5, 2019
attr_reader :event_queue

DEFAULT_BATCH_SIZE = 10
DEFAULT_BATCH_INTERVAL = 30_000
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's with the _?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RuboCop's change, following ruby's style guide.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment about what the unit of this number is.

allow(Optimizely::EventFactory).to receive(:create_log_event).with(any_args)
expected_batch = []
counter = 0
until counter >= 10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we process 11 and make sure the batch only has 10?

@rashidsp rashidsp requested a review from mikeproeng37 August 8, 2019 08:37
Copy link
Contributor

@mikeproeng37 mikeproeng37 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly looks good, I just have a few comments

end

if item.is_a? Optimizely::UserEvent
@logger.log(Logger::DEBUG, "Received add to batch signal. with event: #{item.event['key']}.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not log here as it would be extremely noisy

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not noisy. because when any impression conversion event occurs, then it will be logged, which is OK. I may remove, if you still think it's noisy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think it is noisy. Let's leave it out unless someone actually requests for this level of verbosity.

def run
loop do
if Helpers::DateTimeUtils.create_timestamp > @flushing_interval_deadline
@logger.log(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @msohailhussain mentioned that during testing this was very verbose as it was logged many times. If he is removing from C# SDK it should be removed here as well.

Copy link
Contributor

@msohailhussain msohailhussain Aug 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not the place, it's under this condition, am going to remove.

        if item.nil?
          @logger.log(Logger::DEBUG, 'Empty item, sleeping for 50ms.')
          sleep(0.05)
          next
        end

@rashidsp rashidsp requested a review from a team as a code owner August 15, 2019 10:38
Copy link
Contributor

@mnoman09 mnoman09 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good. Just add conditions that if batch_size is None or batch_size <= 0 than use default batch size. same goes for flush_time_interval

@rashidsp rashidsp requested a review from mikeproeng37 August 17, 2019 12:20
Copy link
Contributor

@mikeproeng37 mikeproeng37 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copy link
Contributor

@aliabbasrizvi aliabbasrizvi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly small changes

@@ -0,0 +1,200 @@
# frozen_string_literal: true

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. add #

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rubocop's rule Layout/EmptyLineAfterMagicComment

class BatchEventProcessor < EventProcessor
# BatchEventProcessor is a batched implementation of the Interface EventProcessor.
# Events passed to the BatchEventProcessor are immediately added to a EventQueue.
# The BatchEventProcessor maintains a single consumer thread that pulls events off of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence seems incomplete.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

attr_reader :event_queue

DEFAULT_BATCH_SIZE = 10
DEFAULT_BATCH_INTERVAL = 30_000
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment about what the unit of this number is.

@batch_size = if (batch_size.is_a? Integer) && positive_number?(batch_size)
batch_size
else
DEFAULT_BATCH_SIZE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A message should be logger here that batch_size is being set to default value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

end

# Reset the deadline if starting a new batch.
@flushing_interval_deadline = (Helpers::DateTimeUtils.create_timestamp + @flush_interval) if @current_batch.empty?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, create_timestamp creates time in local timezone: https://github.com/optimizely/ruby-sdk/blob/master/lib/optimizely/helpers/date_time_utils.rb#L23-L26

Shouldn't that time be in UTC?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@logger.log(Logger::DEBUG, 'Revisions mismatched: Flushing current batch.')
return true
end
# Projects should match
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. New line above this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

@aliabbasrizvi aliabbasrizvi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conditionally approving. Please address feedback before merging.

@msohailhussain msohailhussain changed the title feat(eventProcessor): Adds EventProcessor and BatchEventProcessor feat(eventProcessor): AddsEventProcessor and BatchEventProcessor Aug 20, 2019
@msohailhussain msohailhussain changed the title feat(eventProcessor): AddsEventProcessor and BatchEventProcessor feat(eventProcessor): Add EventProcessor and BatchEventProcessor Aug 20, 2019
@rashidsp rashidsp requested review from aliabbasrizvi and removed request for aliabbasrizvi August 20, 2019 07:08
Copy link
Contributor

@msohailhussain msohailhussain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@mikeproeng37 mikeproeng37 merged commit c6a2bfe into master Aug 22, 2019
@rashidsp rashidsp deleted the rashid/batch-event-processor branch August 29, 2019 19:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants