Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

relocate method in BufferAggregator. #4071

Merged
merged 9 commits into from
Mar 23, 2017
Merged

relocate method in BufferAggregator. #4071

merged 9 commits into from
Mar 23, 2017

Conversation

akashdw
Copy link
Contributor

@akashdw akashdw commented Mar 17, 2017

Fixes #4026.
This pr introduces a new method relocate in BufferAggregator to allow relocation of any cached object.

@gianm @cheddar @himanshug


private NativeMemory getNativeMemory(ByteBuffer buffer)
{
NativeMemory nm = nmCache.get(buffer);
Copy link
Member

Choose a reason for hiding this comment

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

computeIfAbsent() should be used here. See #4034

Copy link
Contributor Author

@akashdw akashdw Mar 17, 2017

Choose a reason for hiding this comment

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

Is this something agreed upon? Also, please explain advantages of computeIfAbsent() compared to this in this case.

Copy link
Member

Choose a reason for hiding this comment

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

  • More readable, intent is clearer: computeIfAbsent speaks for itself, harder to make a mistake.
  • One operation with Map instead of two separate operations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Its a small method, readability and intent is pretty much clear.
Also, This is not a hot method and for now I will stick to the current implementation.

Copy link
Member

Choose a reason for hiding this comment

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

It's not a huge improvement, but it is an improvement by any metric - computeIfAbsent is clearer, shorter, safer, more efficient and the "right" way to perform this kind of task in Java 8+ world. I don't see reasons not to use it in completely new code.

private final Map<Integer, Union> unions = new HashMap<>(); //position in BB -> Union Object
private IdentityHashMap<ByteBuffer, NativeMemory> nmCache = new IdentityHashMap<>();
Copy link
Member

Choose a reason for hiding this comment

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

Make nmCache final

* @param newPosition new position of an item in new ByteBuffer.
* @param newBuffer ByteBuffer to be used.
*/
default void relocate(int oldPostition, int newPosition, ByteBuffer newBuffer)
Copy link
Member

Choose a reason for hiding this comment

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

What is "old buffer"? What exactly does "Relocates any cached objects." mean? How this method could be sensibly defined for any other BufferAggregator, except SketchBufferAggregator?

I start understanding this vaguely after 15 mins of starring at this code, but the description should be more elaborate, and contain examples of code.

Copy link
Member

Choose a reason for hiding this comment

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

Might be more efficient to pass to this method newBuffer and two parallel sequences (lists, iterators, suppliers, whatever) of oldPositions and newPositions. This would allow to optimize relocate() implementation in SketchBufferAggregator specifically, because it won't access nmCache on each update.

Copy link
Contributor Author

@akashdw akashdw Mar 17, 2017

Choose a reason for hiding this comment

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

It will be difficult to put BufferGrouper functionality as a code comment in BufferAggregator, I'll try to put more details without bringing in the BufferGrouper complexities.
I disagree with putting examples in java doc and also don't see other methods doing it.

Relocate is not a hot method(only called when BufferGrouper grows) and I think keeping the existing structure should be okay.

Memory mem = new MemoryRegion(nm, newPosition, maxIntermediateSize);
Union newUnion = (Union) SetOperation.wrap(mem);

Union union = unions.get(oldPosition);
Copy link
Member

Choose a reason for hiding this comment

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

Could be just

unions.remove(oldPosition);
unions.put(newPosition, newUnion);

import java.util.Map;

public class SketchBufferAggregator implements BufferAggregator
{
private final ObjectColumnSelector selector;
private final int size;
private final int maxIntermediateSize;

private NativeMemory nm;

private final Map<Integer, Union> unions = new HashMap<>(); //position in BB -> Union Object
Copy link
Member

Choose a reason for hiding this comment

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

Int2ObjectMap could be used

Copy link
Contributor Author

@akashdw akashdw Mar 17, 2017

Choose a reason for hiding this comment

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

This field was already getting used, not changing the existing fields for now. May be in a separate PR.

Copy link
Member

Choose a reason for hiding this comment

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

"Separate PR" will never happen, in practice. Boxed collections are not going to be replaced with primitive specializations in a single giant PR, we are replacing them along the way, one-by-one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As I mentioned earlier, this field was already there, and this pr is not to refactor SketchBufferAggregator but fixing a bug exists in druid 10. It will be nice if you can provide some good reasons why it has to be changed now.

Copy link
Contributor

Choose a reason for hiding this comment

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

@akashdw this is a simple change and would actually improve performance in this case, so I would +1 this one

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Happy to change it, could someone explain the benefits. @leventov Next time could you please provide reasoning for your suggestions b/c I have to spend more time researching.


import java.nio.ByteBuffer;

public class BufferGrouperTestForSketchAggregation
Copy link
Member

Choose a reason for hiding this comment

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

Please make the test class name end with "Test". There are some environments where the tests could be skipped otherwise (IntelliJ?)

@gianm gianm added this to the 0.10.0 milestone Mar 17, 2017
private final Map<Integer, Union> unions = new HashMap<>(); //position in BB -> Union Object
private final IdentityHashMap<ByteBuffer, NativeMemory> nmCache = new IdentityHashMap<>();
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 we need to maintain this map... at one time we should only have one buffer to work with.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True only if relocate gets a batch update.

Copy link
Contributor Author

@akashdw akashdw Mar 18, 2017

Choose a reason for hiding this comment

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

we need to keep this map b/c BufferAggregator does not guarantee ByteBuffer passed in any of the methods aggregate, relocate is going to be the same. There might be some implementations which can send different ByteBuffer and to support that we need to cache those ByteBuffers.
Given that, I will use the original interface relocate(int oldPosition, int new Position, ByteBuffer newBuffer) b/c diff relocate calls can get diff buffer's.

@@ -113,4 +110,28 @@ public void close()
unions.clear();
}

@Override
public void relocate(int oldPosition, int newPosition, ByteBuffer newBuffer)
Copy link
Contributor

@himanshug himanshug Mar 17, 2017

Choose a reason for hiding this comment

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

I think this method should berelocate(int[] newPositions, ByteBuffer newBuffer) and implementation should discard all old unions and build new ones for each new position and store in the unions map

Copy link
Contributor

@himanshug himanshug Mar 17, 2017

Choose a reason for hiding this comment

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

something like...

void relocate(int[] newPositions, ByteBuffer newBuffer)
{
  unions.clear();
  nm = new NativeMemory(newBuffer);
  for (int i = 0; i < newPositions.length; i++) {
     Memory region = new MemoryRegion(...);
    Union union = wrap;
    unions.put(newPositions[i], union);
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I disagree with relocate(int[] newPositions, ByteBuffer newBuffer) interface b/c we still need old positions to dereference old Union built on top of old ByteBuffer stored in unions map

Copy link
Contributor

Choose a reason for hiding this comment

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

had a conversation offline with @akashdw to clarify and agree on this change

Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't relocate(int[] newPositions, ByteBuffer newBuffer) make what @cheddar wanted to do with quantiles (see #4026 (comment)) impossible?

In that proposal, datasketches quantiles aggregator wouldn't just be caching things that could be recomputed, it would be storing state that needs to be preserved.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Keeping the original interface relocate(int oldPosition, int new Position, ByteBuffer newBuffer) b/c of #4071 (comment)

}

Memory mem = new MemoryRegion(nm, position, maxIntermediateSize);
NativeMemory nativeMemory = getNativeMemory(buf);
Copy link
Contributor

Choose a reason for hiding this comment

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

these changes are not needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True only if relocate gets a batch update.

0.75f,
initialBuckets
);
grouper.init();
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: grouper need to be closed after test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

{
T[] results = (T[]) Array.newInstance(clazz, 2);
BufferAggregator agg = factory.factorizeBuffered(selector);
ByteBuffer myBuf = ByteBuffer.allocate(10040902);
Copy link
Contributor

Choose a reason for hiding this comment

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

The numbers used in this method seem to be something meaningful. Would you explain if they are?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These are random numbers.

List<Map<Integer, Integer>> reloationMaps = new ArrayList<>(aggregators.length);
for (int i = 0; i < aggregators.length ; i++)
{
reloationMaps.add(new HashMap<Integer, Integer>(buckets));
Copy link
Contributor

Choose a reason for hiding this comment

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

This allocates an on-heap hashmap equal to the size of the off-heap table, which spoils the effort to keep memory usage offheap in groupBy v2. That's especially true for smaller bucket sizes (primitives rather than sketches) where this relocation map might be even larger than the off-heap table.

Please avoid creating relocationMaps, you should instead be able to call relocate on the BufferAggregator as entries are moved around.

Copy link
Contributor

Choose a reason for hiding this comment

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

ok, but then BufferAggregator will assume that no position collisions can happen.

but i see the point about keeping most memory usage to be off-heap.

@akashdw let us do the non-batching version then.

Copy link
Contributor

Choose a reason for hiding this comment

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

At least in groupby, the query code will always be growing by relocating data from the old tableBuffer to the new tableBuffer, so collisions shouldn't be an issue.

* @param newBuffer new aggregation buffer.
* @param newBuffer new aggregation buffer.
*/
default void relocate(int oldPosition, int newPosition, ByteBuffer newBuffer, ByteBuffer oldBuffer)
Copy link
Contributor

Choose a reason for hiding this comment

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

The order of args is a little weird here, old is first for position but last for buffer.

* @param oldPosition old position of a cached object before aggregation buffer relocates to a new ByteBuffer.
* @param newPosition new position of a cached object after aggregation buffer relocates to a new ByteBuffer.
* @param newBuffer new aggregation buffer.
* @param newBuffer new aggregation buffer.
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy/paste error in docs.

@@ -24,6 +24,7 @@
import io.druid.query.monomorphicprocessing.RuntimeShapeInspector;

import java.nio.ByteBuffer;
import java.util.Map;
Copy link
Contributor

Choose a reason for hiding this comment

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

Import looks unnecessary.

@@ -87,15 +81,26 @@ public Object get(ByteBuffer buf, int position)
//Note that this is not threadsafe and I don't think it needs to be
private Union getUnion(ByteBuffer buf, int position)
{
Union union = unions.get(position);
Union union = unions.get(buf) != null ? unions.get(buf).get(position) : null;
Copy link
Contributor

Choose a reason for hiding this comment

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

In the common case (union already exists) this will unnecessarily call unions.get(buf) twice.

@Override
public void relocate(int oldPosition, int newPosition, ByteBuffer newBuffer, ByteBuffer oldBuffer)
{
createNewUnion(newBuffer, newPosition, true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be good to also remove the old union so GC can reclaim the memory. And when a ByteBuffer has no more cached unions, you could remove the NativeMemory and Int2ObjectMap for it.

@Override
public void relocate(int oldPosition, int newPosition, ByteBuffer newBuffer, ByteBuffer oldBuffer)
{
createNewUnion(newBuffer, newPosition, true);
Copy link
Contributor

Choose a reason for hiding this comment

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

we need to get rid of old Union object and possibly oldBuffer from the cache (if this was the last relocate call)

}
unionMap.put(position, union);
unions.put(buf, unionMap);
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be moved after line 101

@himanshug
Copy link
Contributor

👍


private NativeMemory getNativeMemory(ByteBuffer buffer)
{
NativeMemory nm = nmCache.get(buffer);
Copy link
Member

Choose a reason for hiding this comment

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

It's not a huge improvement, but it is an improvement by any metric - computeIfAbsent is clearer, shorter, safer, more efficient and the "right" way to perform this kind of task in Java 8+ world. I don't see reasons not to use it in completely new code.

Union union = isWrapped
? (Union) SetOperation.wrap(mem)
: (Union) SetOperation.builder().initMemory(mem).build(size, Family.UNION);
Int2ObjectMap<Union> unionMap = unions.get(buf);
Copy link
Member

Choose a reason for hiding this comment

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

unions.computeIfAbsent(buf, Int2ObjectOpenHashMap::new).put(position, union) could be used 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.

computeIfAbsent() require creating new function on each call and I'm not sure if it would impact performance.

Copy link
Member

Choose a reason for hiding this comment

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

No. In both places where I propose computeIfAbsent() non-capturing lambdas are required, they create only one instance in the JVM: http://stackoverflow.com/questions/27524445/does-a-lambda-expression-create-an-object-on-the-heap-every-time-its-executed#comment43488801_27524445

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did one small performance test, https://gist.github.com/akashdw/9f9b9db11fca602e562ecb47ec3ea24f,

Benchmark Mode Cnt Score Error Units
ComputeIfAbsent.withComputeIfAbsent avgt 25 150.218 ± 4.617 us/op
ComputeIfAbsent.withoutComputeIfAbsent avgt 25 113.956 ± 2.037 us/op

Copy link
Member

Choose a reason for hiding this comment

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

@akashdw have you even tried to analyze the results? Or you supposed that I should do this for you?

  • This benchmark is doing computeIfAbsent() against Int2ObjectOpenHashMap rather than IdentityHashMap, like in the PR
  • Default @Setup level is Level.Trial, that makes little sense for such code. Should be Level.Invocation.
  • The actual reason of the difference, is that Int2ObjectOpenHashMap::new takes the key as capacity of the map. It is a bug in the code that I suggested. It should be k -> new Int2ObjectOpenHashMap().

So the results of this benchmark: https://gist.github.com/leventov/2dabf3563e50012493e030aa69a40be4

ComputeIfAbsent.withComputeIfAbsent     avgt   10  83.784 ± 1.476  us/op
ComputeIfAbsent.withoutComputeIfAbsent  avgt   10  85.201 ± 1.083  us/op

I. e. versions perform identically, that is just as expected, because IdentityHashMap actually doesn't override computeIfAbsent(). The default implementation of computeIfAbsent() does exactly the same calls as you do in the hand-written version. However

  • IdentityHashMap may specialize computeIfAbsent() in the future versions of JDK;
  • At some point of time unions and nmCache could be changed to use e. g. HashMap, which specializes computeIfAbsent(), and the code that already uses computeIfAbsent() will automatically improve;
  • All other advantages of computeIfAbsent() are still in place.

Note: -server is the default option with JDK 8, you don't need to specify it explicitly.

Copy link
Contributor Author

@akashdw akashdw Mar 22, 2017

Choose a reason for hiding this comment

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

@gianm Yes, SketchAggregator.updateUnion is expected to take longer than getUnion and requested optimization might not yield performance difference.
If BufferAggregators called in the order you mentioned in #4071 (comment), then nmCache and unions will contain only one key.

whether or not this optimization is necessary right now.

I think the current changes are in good state, no optimization required and are ready to be merged.

However instead of addressing my original small code quality comment (from last week) that would have taken less than 5 min, @akashdw started a dispute and then a super time-draining benchmark battle

@leventov Are you suggesting to accept change request without doing validation?

To be very clear, one can not simply accept these kind of change requests without verification and validation b/c its about performance/correctness and not about changes you requested which can be done in 5 minutes.

fyi your initial recommendation had bug (b/c probably you didn't validated or benchmarked it )and caught during benchmarking.
Given,SketchAggregator.updateUnion is expected to take longer than getUnion and requested optimization might not yield performance difference, can you please unblock this PR ?

Copy link
Member

Choose a reason for hiding this comment

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

@akashdw

You started to do "performance validation", while taken down earlier suggestion by @himanshug (your coworker, btw), which was much better, both from performance and simplicity points of view, than both versions of code we were arguing about. When I paid attention to that dialogue between you and @himanshug, I realised immediately that the reasons why you decided it is impossible to implement @himanshug's suggestion are not valid and that suggestion is eligible.

So instead of spending a few mins thinking about @himanshug's suggestion, that you should have done if you cared about getUnion()'s performance, because the suggestion obviously makes getUnion() much more faster, you preferred to spend a significant time preparing invalid benchmark and throwing it at me, forcing me to spend a lot of time analyzing it (a required step when doing any benchmarking).

This is a terrible waste of yours and my time and not how communication in Druid PRs is expected to be done.


fyi your initial recommendation had bug (b/c probably you never validated or benchmarked it )and caught during benchmarking

Yes, it's my mistake, and I noted for myself for the future to pay more attention when leaving PR review comments. However bugs are unavoidable in code completely. The fact that you didn't notice it before starting benchmarking also tells that you don't validate review suggestions functionally very attentively.

The fact that benchmark helped to find the mistake is a total coincidence and doesn't mean that benchmarking was useful.


Unblocking PR in it's current state means that I'm taking full responsibility for this situation, that I'm still not going to do. (But it doesn't mean that I impose that it's fully your responsibility, no.) I learned some lessons from this situation and I suggest you to do the same. So currently I suggest you to implement @himanshug / my suggestion. I think it's "compromise enough" because it's not what we were arguing about. Let alone it's just better.

Copy link
Contributor Author

@akashdw akashdw Mar 22, 2017

Choose a reason for hiding this comment

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

@leventov BufferAggregator interface does not tell about the order of execution mentioned here #4071 (comment), now BufferAggregators implementations have two options:

  1. Favor correctness : i.e to support any execution order of BufferAggregator methods. (e.g support aggregate-related methods even without init() being called)
  2. Favor performance : i.e assume relocate method in BufferAggregator. #4071 (comment) execution order and fail aggregate-related methods if methods are not called in correct order.

I choose correctness over performance and Its unfair to block someones PR who disagrees with your choice.

FYI: @himanshug already +1'ed this PR.

Copy link
Contributor

@himanshug himanshug Mar 22, 2017

Choose a reason for hiding this comment

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

@gianm @leventov

The contract of BufferAggregator is that before aggregate(int pos, ByteBuffer buff) is called, caller ensures that init(pos,buff) has been called for that position and buffer.... and that is the end of it. Now, there may or may not be multiple ByteBuffers in the scene at a time .
However, given the current state of Druid, there is only one ByteBuffer there at a time and even groupBy-v2 follows the contract described at #4071 (comment) .
But BufferAggregator contract is more flexible than that. @akashdw made me realize that and to prevent future bugs from happening he stopped making that assumption in the code and created maps. and that is why I gave up on my suggestion and +1d it.

Copy link
Member

Choose a reason for hiding this comment

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

@akashdw @himanshug this is BDUF. Possible redesign of BufferGrouper which won't include "rehash" will make relocate() obsolete as well, so rework of SketchBufferAggregator will be needed anyway. And this BDUF not really safe and couldn't easily be made safe, as I shown above. However both versions are correct now.

@akashdw didn't you notice that you are the Druid contributor who is the most reluctant to making changes suggested by (any) reviewers? General mindset among Druid committers is implementing reviewer's comments or compromise decisions to achieve agreement without extra communication, with high level of common acceptance of the code. Your mindset seems to be to persuade reviewers that your original code is the best. Moreover, your way of persuading is highly disrespectful to reviewer's (my) time.

The shortest and least time-consuming way to resolve this situation now for all parties is for you to spend 15 minutes and implement expectedBuffer and throw away maps. It should be clear for you from the yesterday's evening. Continuing to argue and draining time is entirely your choice.

@cheddar
Copy link
Contributor

cheddar commented Mar 22, 2017

@gianm regarding your questions about the contract. I agree that what you did in groupBy v2 is totally valid and 100%, totally according to contract.

The basic contract is really simply described by the function signatures and that is "you get a ByteBuffer and a position, use the position on the ByteBuffer and your bytes will be there". There is then the other one of "an area of memory is always initialized before being aggregated into", that one isn't called out in the docs as well.

@gianm
Copy link
Contributor

gianm commented Mar 22, 2017

@leventov @akashdw I'm sorry to see such an argument between two people that clearly have strong feelings about Druid!

My vote is to defer to @akashdw's choice for 0.10.0 assuming it is acceptable to committers that are using DataSketches in production (like @himanshug, @cheddar). But I would also like to open an issue for the future to clarify the BufferAggregator contract — it sounds like it's pretty confusing what it allows and doesn't allow — and then use that clarified contract to optimize SketchBufferAggregator more after 0.10.0.

@akashdw @himanshug this is BDUF. Possible redesign of BufferGrouper which won't include "rehash" will make relocate() obsolete as well, so rework of SketchBufferAggregator will be needed anyway.

I don't think it's BDUF, since Query and BufferAggregator are both valid extension points for Druid extensions, and we can't assume we can control all possible implementations. We need the interfaces to be clear about what is allowed and disallowed, and we shouldn't write code that violates them, or else users' site specific custom Queries and Aggregators might not work properly.

Even future code written as part of Druid might not work properly, after we all put this behind us and forget that SketchBufferAggregator doesn't quite follow the contract 100%. This is actually how we got into this situation in the first place! When I wrote groupBy v2, I used BufferAggregator interface in a way that other queries hadn't done before, which I thought satisfied the contract, but broke SketchBufferAggregator. Arguably, the old version of SketchBufferAggregator didn't follow the contract, but it was "OK" until the change to groupBy.

So, I think the maps are useful and it's valid choice to be a little "flexible" until the BufferAggregator interface is clarified.

Copy link
Contributor

@gianm gianm left a comment

Choose a reason for hiding this comment

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

LGTM

@akashdw have you had a chance to run this in prod to verify it fixes the original issue #4026?

@gianm
Copy link
Contributor

gianm commented Mar 23, 2017

@leventov I understand this patch may not be perfect but do you see show-stopper issues that we cannot resolve in future releases?

@akashdw
Copy link
Contributor Author

akashdw commented Mar 23, 2017

@gianm testing this patch in one of the production environment, will let you know once done with the testing.

@gianm
Copy link
Contributor

gianm commented Mar 23, 2017

Thanks @akashdw, please keep us posted.

@akashdw
Copy link
Contributor Author

akashdw commented Mar 23, 2017

@gianm tested this patch in one of our environment and I don't see relocate issue with GroupByV2 anymore.

@gianm
Copy link
Contributor

gianm commented Mar 23, 2017

Spoke with @leventov offline. I'll merge this since it has +1s from me and @himanshug.

@gianm gianm merged commit ff7f90b into apache:master Mar 23, 2017
gianm pushed a commit to gianm/druid that referenced this pull request Mar 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants