Skip to content

Streaming bulk operations - #112

Merged
chrismwendt merged 12 commits into
masterfrom
streaming-bulk
Sep 18, 2015
Merged

chrismwendt merged 12 commits into
masterfrom
streaming-bulk

Conversation

@chrismwendt

Copy link
Copy Markdown
Contributor

This adds streaming bulk functionality via Ruby's Enumerators.

This does not to alter any of the existing methods in backwards incompatible ways so that a conversation about making the API more consistent could take place later. Because of that, some of the small operation methods had to be nearly duplicated.

This solves #92.

/cc @TwP @grantr

Comment thread lib/elastomer/client/bulk.rb Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is there a simpler way to essentially map over an Enumerator than by constructing a new one and yielding each element of the original, as I did here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One thought on this method is to not return an Enumerator. Instead, accept a block and perform the iteration directly with this method.

def bulk_stream_items(ops, params = {})
  stats = {
    "took" => 0,
    "errors" => false,
    "success" => 0,
    "failure" => 0
  }

  bulk_stream_responses(ops, params).each do |response|
    stats["took"] += response["took"]
    stats["errors"] |= response["errors"]

    response["items"].each do |item|
      if is_ok?(item)
        stats["success"] += 1
      else
        stats["failure"] += 1
      end
      yield item
    end
  end

  return stats
end

I've also added a hash of interesting numbers as the return value from this method. The is_ok? method does not exist yet, but it would check the HTTP status returned with each item.

def is_ok?(item)
  item["status"].between?(200, 299)
end

Thoughts on this approach?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The client might want to retry failed documents, in which case they would need access to at least the failed response items. Maybe it's worth having a separate convenient method called bulk_stream_stats which does exactly what you suggest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The client does have access to the failed items during the iteration. When yield item is called, the client can also check the status code of that particular operation.

Returning the stats hash is there just to be nice to the end user.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My apologies, I read over your code too quickly and missed that yield item 🙇

I like the convenience of returning stats, and the block syntax would look almost the same as calling each on the Enumerator. I'll implement it like you showed.

This reverts commit 1c977c0.

Elasticsearch throws an error when `_score` is present:

```
Elastomer::Client::ServerError: IllegalArgumentException[Action/metadata line [1] contains an unknown parameter [_score]]
```
Comment thread lib/elastomer/client/bulk.rb Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not a huge fan of the Bulk::index / Bulk::delete syntax here. My proposal is this:

ops = [
  [:index, document1, {:_type => "foo", :_id => 1}],
  [:create, document2],
  [:delete, {:_type => "bar", :_id => 42}]
]

The first item in the list is the bulk action to take. This corresponds to one of the action methods on the Bulk instance. The second item in the list is a document (or the params in the case of :delete). And thirdly comes the optional params.

Inside the Enumerator we have:

Enumerator.new do |yielder|
  ops.each do |action, *args|
    response = bulk_obj.send(action, *args)
    yielder.yield response unless response.nil?
  end
  response = bulk_obj.call
  yielder.yield response unless response.nil?
end

The Ruby send method is being used here to call the action method on the bulk_obj and passing the *args to the method.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's a pretty clever use of send to reuse the existing methods! I'll implement that now.

Comment thread lib/elastomer/client/bulk.rb Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This can be reverted back to an instance method.

This is a continuation of c90963b.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we document what the item typically looks like?

@chrismwendt

Copy link
Copy Markdown
Contributor Author

@TwP: I reverted those class methods back to instance methods.

@grantr: I added a sample response item to the docs.

I also bumped the version and updated the changelog.

@TwP

TwP commented Sep 18, 2015

Copy link
Copy Markdown
Contributor

👍 :shipit: ✨ and that's enough emoji for now.

chrismwendt pushed a commit that referenced this pull request Sep 18, 2015
@chrismwendt
chrismwendt merged commit a19ffa5 into master Sep 18, 2015
@chrismwendt
chrismwendt deleted the streaming-bulk branch September 18, 2015 19:14
@grantr

grantr commented Sep 18, 2015

Copy link
Copy Markdown
Contributor

This is great stuff, useful and easy to use. 👍

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.

4 participants