-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
ESQL: Page shouldn't close a block twice #100370
Changes from 5 commits
a7cb6ab
63de76d
7932c05
5cc7539
86ef454
e3415a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
pr: 100370 | ||
summary: "ESQL: Page shouldn't close a block twice" | ||
area: ES|QL | ||
type: bug | ||
issues: | ||
- 100356 | ||
- 100365 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
|
||
import java.io.IOException; | ||
import java.util.Arrays; | ||
import java.util.IdentityHashMap; | ||
import java.util.Objects; | ||
|
||
/** | ||
|
@@ -88,9 +89,7 @@ private Page(Page prev, Block[] toAdd) { | |
this.positionCount = prev.positionCount; | ||
|
||
this.blocks = Arrays.copyOf(prev.blocks, prev.blocks.length + toAdd.length); | ||
for (int i = 0; i < toAdd.length; i++) { | ||
this.blocks[prev.blocks.length + i] = toAdd[i]; | ||
} | ||
System.arraycopy(toAdd, 0, this.blocks, prev.blocks.length, toAdd.length); | ||
} | ||
|
||
public Page(StreamInput in) throws IOException { | ||
|
@@ -169,8 +168,8 @@ public Page appendPage(Page toAdd) { | |
@Override | ||
public int hashCode() { | ||
int result = Objects.hash(positionCount); | ||
for (int i = 0; i < blocks.length; i++) { | ||
result = 31 * result + Objects.hashCode(blocks[i]); | ||
for (Block block : blocks) { | ||
result = 31 * result + Objects.hashCode(block); | ||
} | ||
return result; | ||
} | ||
|
@@ -222,6 +221,43 @@ public void writeTo(StreamOutput out) throws IOException { | |
*/ | ||
public void releaseBlocks() { | ||
blocksReleased = true; | ||
Releasables.closeExpectNoException(blocks); | ||
// blocks can be used as multiple columns | ||
var map = new IdentityHashMap<Block, Object>(mapSize(blocks.length)); | ||
var DUMMY = new Object(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That could be moved into a static final var. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or...
|
||
for (Block b : blocks) { | ||
if (map.putIfAbsent(b, DUMMY) == null) { | ||
Releasables.closeExpectNoException(b); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this improves the issue, but there still can be problems because non-identical blocks can be backed by the same vector or array. Additionally, we create and populate a new hash map on each page release; that's probably quite expensive. We can merge this to fix the problem right now, but IMO should mark this with a todo comment to remove this logic once possible. We want ref counting, this will fix the problem more idiomatically. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Why would they?
I've added a check to skip the release once it's one - we can change that if we want a page to only be released once. |
||
} | ||
|
||
/** | ||
* Returns a Page from the given blocks and closes all blocks that are not included, from the current Page. | ||
* That is, allows clean-up of the current page _after_ external manipulation of the blocks. | ||
* The current page should no longer be used and be considered closed. | ||
*/ | ||
public Page newPageAndRelease(Block... keep) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've introduced this method for handling the case of reusing some blocks from an old page into a new one. |
||
blocksReleased = true; | ||
|
||
var newPage = new Page(positionCount, keep); | ||
var map = new IdentityHashMap<Block, Object>(mapSize(keep.length)); | ||
var DUMMY = new Object(); | ||
|
||
// create identity set | ||
for (Block b : keep) { | ||
map.putIfAbsent(b, DUMMY); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Subjective:
|
||
} | ||
// close blocks that have been left out | ||
for (Block b : blocks) { | ||
if (map.containsKey(b) == false) { | ||
Releasables.closeExpectNoException(b); | ||
} | ||
} | ||
|
||
return newPage; | ||
} | ||
|
||
static int mapSize(int expectedSize) { | ||
return expectedSize < 2 ? expectedSize + 1 : (int) (expectedSize / 0.75 + 1.0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small improvement.