diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java index 6998cc20e9fe0..a7d397fcfb98e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlockBuilder.java @@ -193,7 +193,6 @@ public BooleanBlock build() { } } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); return block; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java index 5dc52f3051e13..d9926227e1c60 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVectorBuilder.java @@ -59,7 +59,6 @@ public BooleanVector build() { vector = new BooleanArrayVector(values, valueCount, blockFactory); } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); return vector; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java index bbde59fe6236f..426731ac06798 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayBlock.java @@ -10,6 +10,7 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; import java.util.BitSet; import java.util.stream.IntStream; @@ -110,6 +111,7 @@ public String toString() { @Override public void close() { - blockFactory.adjustBreaker(-ramBytesUsed(), true); + blockFactory.adjustBreaker(-(ramBytesUsed() - values.ramBytesUsed()), true); + Releasables.closeExpectNoException(values); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java index 92027a6f52ee1..fc32519a6acce 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefArrayVector.java @@ -85,6 +85,7 @@ public String toString() { @Override public void close() { + blockFactory.adjustBreaker(-BASE_RAM_BYTES_USED, true); Releasables.closeExpectNoException(values); } } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java index 646b2700f7379..0f1934f674d69 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlockBuilder.java @@ -10,6 +10,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; /** * Block build of BytesRefBlocks. @@ -195,8 +196,10 @@ public BytesRefBlock build() { finish(); BytesRefBlock block; if (hasNonNullValue && positionCount == 1 && valueCount == 1) { - block = new ConstantBytesRefVector(values.get(0, new BytesRef()), 1, blockFactory).asBlock(); + block = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory).asBlock(); + Releasables.closeExpectNoException(values); } else { + estimatedBytes += values.ramBytesUsed(); if (isDense() && singleValued()) { block = new BytesRefArrayVector(values, positionCount, blockFactory).asBlock(); } else { @@ -204,7 +207,6 @@ public BytesRefBlock build() { } } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); return block; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java index dd303b32ccfd0..be753771ac961 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVectorBuilder.java @@ -10,6 +10,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; /** * Block build of BytesRefBlocks. @@ -55,12 +56,13 @@ protected void growValuesArray(int newSize) { public BytesRefVector build() { BytesRefVector vector; if (valueCount == 1) { - vector = new ConstantBytesRefVector(values.get(0, new BytesRef()), 1, blockFactory); + vector = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory); + Releasables.closeExpectNoException(values); } else { + estimatedBytes = values.ramBytesUsed(); vector = new BytesRefArrayVector(values, valueCount, blockFactory); } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); return vector; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java index c0bc9c79480b7..a97f58f3924b1 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlockBuilder.java @@ -193,7 +193,6 @@ public DoubleBlock build() { } } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); return block; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java index 657f1ca547a05..8112c5458280f 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVectorBuilder.java @@ -59,7 +59,6 @@ public DoubleVector build() { vector = new DoubleArrayVector(values, valueCount, blockFactory); } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); return vector; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java index ae6a98330acdf..53d379d715c9b 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlockBuilder.java @@ -193,7 +193,6 @@ public IntBlock build() { } } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); return block; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java index 8d920b069440d..8bf4a4a96c5cb 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVectorBuilder.java @@ -59,7 +59,6 @@ public IntVector build() { vector = new IntArrayVector(values, valueCount, blockFactory); } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); return vector; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java index 28e82af071ba3..a378b382ce31e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlockBuilder.java @@ -193,7 +193,6 @@ public LongBlock build() { } } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); return block; } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java index 5033684e32833..10daed94a966e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVectorBuilder.java @@ -59,7 +59,6 @@ public LongVector build() { vector = new LongArrayVector(values, valueCount, blockFactory); } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); return vector; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java index c3a7f004641a8..6d92826038c03 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java @@ -76,6 +76,11 @@ public CircuitBreaker breaker() { return breaker; } + // For testing + public BigArrays bigArrays() { + return bigArrays; + } + /** * Adjust the circuit breaker with the given delta, if the delta is * negative, or checkBreaker is false, the breaker will be adjusted @@ -238,7 +243,7 @@ public BytesRefBlock newBytesRefArrayBlock( MvOrdering mvOrdering ) { var b = new BytesRefArrayBlock(values, positionCount, firstValueIndexes, nulls, mvOrdering, this); - adjustBreaker(b.ramBytesUsed(), true); + adjustBreaker(b.ramBytesUsed() - values.ramBytesUsed(), true); return b; } @@ -248,11 +253,13 @@ public BytesRefVector.Builder newBytesRefVectorBuilder(int estimatedSize) { public BytesRefVector newBytesRefArrayVector(BytesRefArray values, int positionCount) { var b = new BytesRefArrayVector(values, positionCount, this); - adjustBreaker(b.ramBytesUsed(), true); + adjustBreaker(b.ramBytesUsed() - values.ramBytesUsed(), true); return b; } public BytesRefBlock newConstantBytesRefBlockWith(BytesRef value, int positions) { - return new ConstantBytesRefVector(value, positions, this).asBlock(); + var b = new ConstantBytesRefVector(value, positions, this).asBlock(); + adjustBreaker(b.ramBytesUsed(), true); + return b; } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st index b6487c2409a84..dd3a914eae9f7 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayBlock.java.st @@ -11,6 +11,7 @@ $if(BytesRef)$ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; $else$ import org.apache.lucene.util.RamUsageEstimator; @@ -131,6 +132,11 @@ $endif$ @Override public void close() { + $if(BytesRef)$ + blockFactory.adjustBreaker(-(ramBytesUsed() - values.ramBytesUsed()), true); + Releasables.closeExpectNoException(values); + $else$ blockFactory.adjustBreaker(-ramBytesUsed(), true); + $endif$ } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st index 7b5d049ec1f08..bb7c10f8bb432 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-ArrayVector.java.st @@ -110,6 +110,7 @@ $endif$ @Override public void close() { $if(BytesRef)$ + blockFactory.adjustBreaker(-BASE_RAM_BYTES_USED, true); Releasables.closeExpectNoException(values); $else$ super.close(); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st index 9e548eda407c4..34c528bff1a58 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-BlockBuilder.java.st @@ -11,6 +11,7 @@ $if(BytesRef)$ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; $else$ import java.util.Arrays; @@ -249,8 +250,10 @@ $endif$ $Type$Block block; if (hasNonNullValue && positionCount == 1 && valueCount == 1) { $if(BytesRef)$ - block = new ConstantBytesRefVector(values.get(0, new BytesRef()), 1, blockFactory).asBlock(); + block = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory).asBlock(); + Releasables.closeExpectNoException(values); } else { + estimatedBytes += values.ramBytesUsed(); $else$ block = new Constant$Type$Vector(values[0], 1, blockFactory).asBlock(); } else { @@ -265,7 +268,6 @@ $endif$ } } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(block.ramBytesUsed() - estimatedBytes, true); return block; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st index 09c166789f312..2a5f4a15b8f8a 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-VectorBuilder.java.st @@ -11,6 +11,7 @@ $if(BytesRef)$ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.core.Releasables; $else$ import java.util.Arrays; @@ -85,8 +86,10 @@ $endif$ $Type$Vector vector; if (valueCount == 1) { $if(BytesRef)$ - vector = new ConstantBytesRefVector(values.get(0, new BytesRef()), 1, blockFactory); + vector = new ConstantBytesRefVector(BytesRef.deepCopyOf(values.get(0, new BytesRef())), 1, blockFactory); + Releasables.closeExpectNoException(values); } else { + estimatedBytes = values.ramBytesUsed(); $else$ vector = new Constant$Type$Vector(values[0], 1, blockFactory); } else { @@ -97,7 +100,6 @@ $endif$ vector = new $Type$ArrayVector(values, valueCount, blockFactory); } // update the breaker with the actual bytes used. - // TODO: verify that this can also give back blockFactory.adjustBreaker(vector.ramBytesUsed() - estimatedBytes, true); return vector; } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java index 8c684daab31ff..c89d39ecc9032 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java @@ -12,10 +12,15 @@ import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.compute.CountingCircuitBreaker; import org.elasticsearch.core.Releasable; import org.elasticsearch.core.Releasables; +import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.test.ESTestCase; +import org.junit.After; +import org.junit.Before; import java.util.ArrayList; import java.util.Arrays; @@ -31,51 +36,57 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class BasicBlockTests extends ESTestCase { - public void testEmpty() { - assertThat( - new IntArrayBlock(new int[] {}, 0, new int[] {}, new BitSet(), randomFrom(Block.MvOrdering.values())).getPositionCount(), - is(0) - ); - assertThat(IntBlock.newBlockBuilder(0).build().getPositionCount(), is(0)); - assertThat(new IntArrayVector(new int[] {}, 0).getPositionCount(), is(0)); - assertThat(IntVector.newVectorBuilder(0).build().getPositionCount(), is(0)); + final CircuitBreaker breaker = new CountingCircuitBreaker("ESQL-test-breaker"); + final BlockFactory blockFactory = BlockFactory.getInstance(breaker, bigArrays()); - assertThat( - new LongArrayBlock(new long[] {}, 0, new int[] {}, new BitSet(), randomFrom(Block.MvOrdering.values())).getPositionCount(), - is(0) - ); - assertThat(LongBlock.newBlockBuilder(0).build().getPositionCount(), is(0)); - assertThat(new LongArrayVector(new long[] {}, 0).getPositionCount(), is(0)); - assertThat(LongVector.newVectorBuilder(0).build().getPositionCount(), is(0)); + BigArrays bigArrays() { + var breakerService = mock(CircuitBreakerService.class); + when(breakerService.getBreaker(CircuitBreaker.REQUEST)).thenReturn(breaker); + return new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, breakerService); + } - assertThat( - new DoubleArrayBlock(new double[] {}, 0, new int[] {}, new BitSet(), randomFrom(Block.MvOrdering.values())).getPositionCount(), - is(0) - ); - assertThat(DoubleBlock.newBlockBuilder(0).build().getPositionCount(), is(0)); - assertThat(new DoubleArrayVector(new double[] {}, 0).getPositionCount(), is(0)); - assertThat(DoubleVector.newVectorBuilder(0).build().getPositionCount(), is(0)); - - var emptyArray = new BytesRefArray(0, BigArrays.NON_RECYCLING_INSTANCE); - assertThat( - new BytesRefArrayBlock(emptyArray, 0, new int[] {}, new BitSet(), randomFrom(Block.MvOrdering.values())).getPositionCount(), - is(0) - ); - assertThat(BytesRefBlock.newBlockBuilder(0).build().getPositionCount(), is(0)); - assertThat(new BytesRefArrayVector(emptyArray, 0).getPositionCount(), is(0)); - assertThat(BytesRefVector.newVectorBuilder(0).build().getPositionCount(), is(0)); - - assertThat( - new BooleanArrayBlock(new boolean[] {}, 0, new int[] {}, new BitSet(), randomFrom(Block.MvOrdering.values())) - .getPositionCount(), - is(0) + @Before + @After + public void checkBreaker() { + assertThat(breaker.getUsed(), is(0L)); + } + + public void testEmpty() { + testEmpty(blockFactory); + } + + public void testEmptyNonBreakingFactory() { + testEmpty(BlockFactory.getNonBreakingInstance()); + } + + public void testEmpty(BlockFactory bf) { + assertZeroPositionsAndRelease(bf.newIntArrayBlock(new int[] {}, 0, new int[] {}, new BitSet(), randomOrdering())); + assertZeroPositionsAndRelease(IntBlock.newBlockBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newIntArrayVector(new int[] {}, 0)); + assertZeroPositionsAndRelease(IntVector.newVectorBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newLongArrayBlock(new long[] {}, 0, new int[] {}, new BitSet(), randomOrdering())); + assertZeroPositionsAndRelease(LongBlock.newBlockBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newLongArrayVector(new long[] {}, 0)); + assertZeroPositionsAndRelease(LongVector.newVectorBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newDoubleArrayBlock(new double[] {}, 0, new int[] {}, new BitSet(), randomOrdering())); + assertZeroPositionsAndRelease(DoubleBlock.newBlockBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newDoubleArrayVector(new double[] {}, 0)); + assertZeroPositionsAndRelease(DoubleVector.newVectorBuilder(0, bf).build()); + assertZeroPositionsAndRelease( + bf.newBytesRefArrayBlock(new BytesRefArray(0, bf.bigArrays()), 0, new int[] {}, new BitSet(), randomOrdering()) ); - assertThat(BooleanBlock.newBlockBuilder(0).build().getPositionCount(), is(0)); - assertThat(new BooleanArrayVector(new boolean[] {}, 0).getPositionCount(), is(0)); - assertThat(BooleanVector.newVectorBuilder(0).build().getPositionCount(), is(0)); + assertZeroPositionsAndRelease(BytesRefBlock.newBlockBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newBytesRefArrayVector(new BytesRefArray(0, bf.bigArrays()), 0)); + assertZeroPositionsAndRelease(BytesRefVector.newVectorBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newBooleanArrayBlock(new boolean[] {}, 0, new int[] {}, new BitSet(), randomOrdering())); + assertZeroPositionsAndRelease(BooleanBlock.newBlockBuilder(0, bf).build()); + assertZeroPositionsAndRelease(bf.newBooleanArrayVector(new boolean[] {}, 0)); + assertZeroPositionsAndRelease(BooleanVector.newVectorBuilder(0, bf).build()); } public void testSmallSingleValueDenseGrowthInt() { @@ -144,9 +155,6 @@ static void assertSingleValueDenseBlock(Block initialBlock) { } } - static final CircuitBreaker breaker = new CountingCircuitBreaker("ESQL-test-breaker"); - static final BlockFactory blockFactory = BlockFactory.getInstance(breaker, BigArrays.NON_RECYCLING_INSTANCE); - public void testIntBlock() { for (int i = 0; i < 1000; i++) { assertThat(breaker.getUsed(), is(0L)); @@ -168,7 +176,7 @@ public void testIntBlock() { int pos = block.getInt(randomPosition(positionCount)); assertThat(pos, is(block.getInt(pos))); assertSingleValueDenseBlock(block); - assertBreakerAndRelease(block); + releaseAndAssertBreaker(block); if (positionCount > 1) { assertNullValues( @@ -213,7 +221,7 @@ public void testConstantIntBlock() { assertThat(value, is(block.getInt(randomPosition(positionCount)))); assertThat(block.isNull(randomPosition(positionCount)), is(false)); assertSingleValueDenseBlock(block); - assertBreakerAndRelease(block); + releaseAndAssertBreaker(block); } } @@ -237,7 +245,7 @@ public void testLongBlock() { int pos = (int) block.getLong(randomPosition(positionCount)); assertThat((long) pos, is(block.getLong(pos))); assertSingleValueDenseBlock(block); - assertBreakerAndRelease(block); + releaseAndAssertBreaker(block); if (positionCount > 1) { assertNullValues( @@ -282,7 +290,7 @@ public void testConstantLongBlock() { assertThat(value, is(block.getLong(randomPosition(positionCount)))); assertThat(block.isNull(randomPosition(positionCount)), is(false)); assertSingleValueDenseBlock(block); - assertBreakerAndRelease(block); + releaseAndAssertBreaker(block); } } @@ -870,8 +878,17 @@ private static void assertNullVal assertFalse(block.isNull(randomNonNullPosition)); } - static void assertBreakerAndRelease(T data) { - assertThat(breaker.getUsed(), is(equalTo(data.ramBytesUsed()))); + void assertZeroPositionsAndRelease(Block block) { + assertThat(block.getPositionCount(), is(0)); + releaseAndAssertBreaker(block); + } + + void assertZeroPositionsAndRelease(Vector vector) { + assertThat(vector.getPositionCount(), is(0)); + releaseAndAssertBreaker(vector); + } + + void releaseAndAssertBreaker(T data) { Releasables.closeExpectNoException(data); assertThat(breaker.getUsed(), is(0L)); } @@ -879,4 +896,8 @@ static void assertBreakerAndRelease(T data) static int randomPosition(int positionCount) { return positionCount == 1 ? 0 : randomIntBetween(0, positionCount - 1); } + + static Block.MvOrdering randomOrdering() { + return randomFrom(Block.MvOrdering.values()); + } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java new file mode 100644 index 0000000000000..0a727dcf1fe29 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BlockFactoryTests.java @@ -0,0 +1,521 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import org.apache.lucene.util.Accountable; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.BytesRefArray; +import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.compute.CountingCircuitBreaker; +import org.elasticsearch.core.Releasable; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.indices.breaker.CircuitBreakerService; +import org.elasticsearch.test.ESTestCase; +import org.junit.After; +import org.junit.Before; + +import java.util.BitSet; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +// BlockFactory is used and effectively tested in many other places, but this class contains tests +// more specific to the factory implementation itself (and not necessarily tested elsewhere). +// @com.carrotsearch.randomizedtesting.annotations.Repeat(iterations = 1000) +public class BlockFactoryTests extends ESTestCase { + + final CircuitBreaker breaker = new CountingCircuitBreaker("ESQL-test-breaker"); + final BigArrays bigArrays = bigArrays(); + final BlockFactory blockFactory = BlockFactory.getInstance(breaker, bigArrays); + + BigArrays bigArrays() { + var breakerService = mock(CircuitBreakerService.class); + when(breakerService.getBreaker(CircuitBreaker.REQUEST)).thenReturn(breaker); + return new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, breakerService); + } + + @Before + @After + public void checkBreaker() { + assertThat(breaker.getUsed(), is(0L)); + } + + public void testIntBlockBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newIntBlockBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newIntArrayBlock(new int[] {}, 0, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testIntBlockBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newIntBlockBuilder(randomIntBetween(0, 2048)); + builder.appendInt(randomInt()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newIntArrayBlock(new int[] { randomInt() }, 1, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + + block = blockFactory.newConstantIntBlockWith(randomInt(), randomIntBetween(1, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testIntBlockBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newIntBlockBuilder(randomIntBetween(0, 2048)); + + builder.appendInt(randomInt()); + if (randomBoolean()) { // null-ness + builder.appendNull(); + } + if (randomBoolean()) { // mv-ness + builder.beginPositionEntry(); + builder.appendInt(randomInt()); + builder.appendInt(randomInt()); + builder.endPositionEntry(); + } + builder.appendInt(randomInt()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + } + } + + public void testIntVectorBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newIntVectorBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newIntArrayVector(new int[] {}, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testIntVectorBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newIntVectorBuilder(randomIntBetween(0, 2048)); + builder.appendInt(randomInt()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newIntArrayVector(new int[] { randomInt() }, 1); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newConstantIntBlockWith(randomInt(), randomIntBetween(1, 2048)).asVector(); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testIntVectorBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newIntVectorBuilder(randomIntBetween(0, 2048)); + builder.appendInt(randomInt()); + if (randomBoolean()) { // constant-ness or not + builder.appendInt(randomInt()); + } + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + } + } + + public void testLongBlockBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newLongBlockBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newLongArrayBlock(new long[] {}, 0, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testLongBlockBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newLongBlockBuilder(randomIntBetween(0, 2048)); + builder.appendLong(randomLong()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newLongArrayBlock(new long[] { randomLong() }, 1, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + + block = blockFactory.newConstantLongBlockWith(randomLong(), randomIntBetween(1, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testLongBlockBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newLongBlockBuilder(randomIntBetween(0, 2048)); + + builder.appendLong(randomLong()); + if (randomBoolean()) { // null-ness + builder.appendNull(); + } + if (randomBoolean()) { // mv-ness + builder.beginPositionEntry(); + builder.appendLong(randomInt()); + builder.appendLong(randomInt()); + builder.endPositionEntry(); + } + builder.appendLong(randomLong()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + } + } + + public void testLongVectorBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newLongVectorBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newLongArrayVector(new long[] {}, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testLongVectorBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newLongVectorBuilder(randomIntBetween(0, 2048)); + builder.appendLong(randomLong()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newLongArrayVector(new long[] { randomLong() }, 1); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newConstantLongBlockWith(randomLong(), randomIntBetween(1, 2048)).asVector(); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testLongVectorBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newLongVectorBuilder(randomIntBetween(0, 2048)); + builder.appendLong(randomLong()); + if (randomBoolean()) { // constant-ness or not + builder.appendLong(randomLong()); + } + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + } + } + + public void testDoubleBlockBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newDoubleBlockBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newDoubleArrayBlock(new double[] {}, 0, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testDoubleBlockBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newDoubleBlockBuilder(randomIntBetween(0, 2048)); + builder.appendDouble(randomDouble()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newDoubleArrayBlock(new double[] { randomDouble() }, 1, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + + block = blockFactory.newConstantDoubleBlockWith(randomDouble(), randomIntBetween(1, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testDoubleBlockBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newDoubleBlockBuilder(randomIntBetween(0, 2048)); + + builder.appendDouble(randomDouble()); + if (randomBoolean()) { // null-ness + builder.appendNull(); + } + if (randomBoolean()) { // mv-ness + builder.beginPositionEntry(); + builder.appendDouble(randomDouble()); + builder.appendDouble(randomDouble()); + builder.endPositionEntry(); + } + builder.appendDouble(randomDouble()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + } + } + + public void testDoubleVectorBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newDoubleVectorBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newDoubleArrayVector(new double[] {}, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testDoubleVectorBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newDoubleVectorBuilder(randomIntBetween(0, 2048)); + builder.appendDouble(randomDouble()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newDoubleArrayVector(new double[] { randomDouble() }, 1); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newConstantDoubleBlockWith(randomDouble(), randomIntBetween(1, 2048)).asVector(); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testDoubleVectorBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newDoubleVectorBuilder(randomIntBetween(0, 2048)); + builder.appendDouble(randomDouble()); + if (randomBoolean()) { // constant-ness or not + builder.appendDouble(randomDouble()); + } + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + } + } + + public void testBooleanBlockBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newBooleanBlockBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newBooleanArrayBlock(new boolean[] {}, 0, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testBooleanBlockBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newBooleanBlockBuilder(randomIntBetween(0, 2048)); + builder.appendBoolean(randomBoolean()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + block = blockFactory.newBooleanArrayBlock(new boolean[] { randomBoolean() }, 1, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + + block = blockFactory.newConstantBooleanBlockWith(randomBoolean(), randomIntBetween(1, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testBooleanBlockBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newBooleanBlockBuilder(randomIntBetween(0, 2048)); + + builder.appendBoolean(randomBoolean()); + if (randomBoolean()) { // null-ness + builder.appendNull(); + } + if (randomBoolean()) { // mv-ness + builder.beginPositionEntry(); + builder.appendBoolean(randomBoolean()); + builder.appendBoolean(randomBoolean()); + builder.endPositionEntry(); + } + builder.appendBoolean(randomBoolean()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + } + } + + public void testBooleanVectorBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newBooleanVectorBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newBooleanArrayVector(new boolean[] {}, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testBooleanVectorBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newBooleanVectorBuilder(randomIntBetween(0, 2048)); + builder.appendBoolean(randomBoolean()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newBooleanArrayVector(new boolean[] { randomBoolean() }, 1); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newConstantBooleanBlockWith(randomBoolean(), randomIntBetween(1, 2048)).asVector(); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testBooleanVectorBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newBooleanVectorBuilder(randomIntBetween(0, 2048)); + builder.appendBoolean(randomBoolean()); + if (randomBoolean()) { // constant-ness or not + builder.appendBoolean(randomBoolean()); + } + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + } + } + + public void testBytesRefBlockBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newBytesRefBlockBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + var emptyArray = new BytesRefArray(0, bigArrays); + block = blockFactory.newBytesRefArrayBlock(emptyArray, 0, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testBytesRefBlockBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newBytesRefBlockBuilder(randomIntBetween(0, 2048)); + builder.appendBytesRef(randomBytesRef()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + + var array = new BytesRefArray(1, bigArrays); + array.append(randomBytesRef()); + block = blockFactory.newBytesRefArrayBlock(array, 1, new int[] {}, new BitSet(), randomOrdering()); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + + block = blockFactory.newConstantBytesRefBlockWith(randomBytesRef(), randomIntBetween(1, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(block); + } + + public void testBytesRefBlockBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newBytesRefBlockBuilder(randomIntBetween(0, 2048)); + + builder.appendBytesRef(randomBytesRef()); + if (randomBoolean()) { // null-ness + builder.appendNull(); + } + if (randomBoolean()) { // mv-ness + builder.beginPositionEntry(); + builder.appendBytesRef(randomBytesRef()); + builder.appendBytesRef(randomBytesRef()); + builder.endPositionEntry(); + } + builder.appendBytesRef(randomBytesRef()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var block = builder.build(); + releaseAndAssertBreaker(block); + } + } + + public void testBytesRefVectorBuilderWithPossiblyLargeEstimateEmpty() { + var builder = blockFactory.newBytesRefVectorBuilder(randomIntBetween(0, 2048)); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + var emptyArray = new BytesRefArray(0, bigArrays); + vector = blockFactory.newBytesRefArrayVector(emptyArray, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testBytesRefVectorBuilderWithPossiblyLargeEstimateSingle() { + var builder = blockFactory.newBytesRefVectorBuilder(randomIntBetween(0, 2048)); + builder.appendBytesRef(randomBytesRef()); + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + + var array = new BytesRefArray(1, bigArrays); + array.append(randomBytesRef()); + vector = blockFactory.newBytesRefArrayVector(array, 0); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + + vector = blockFactory.newConstantBytesRefBlockWith(randomBytesRef(), randomIntBetween(1, 2048)).asVector(); + assertThat(breaker.getUsed(), greaterThan(0L)); + releaseAndAssertBreaker(vector); + } + + public void testBytesRefVectorBuilderWithPossiblyLargeEstimateRandom() { + for (int i = 0; i < 1000; i++) { + assertThat(breaker.getUsed(), is(0L)); + var builder = blockFactory.newBytesRefVectorBuilder(randomIntBetween(0, 2048)); + builder.appendBytesRef(randomBytesRef()); + if (randomBoolean()) { // constant-ness or not + builder.appendBytesRef(randomBytesRef()); + } + assertThat(breaker.getUsed(), greaterThan(0L)); + var vector = builder.build(); + releaseAndAssertBreaker(vector); + } + } + + static BytesRef randomBytesRef() { + return new BytesRef(randomByteArrayOfLength(between(1, 20))); + } + + static Block.MvOrdering randomOrdering() { + return randomFrom(Block.MvOrdering.values()); + } + + void releaseAndAssertBreaker(T data) { + Releasables.closeExpectNoException(data); + assertThat(breaker.getUsed(), is(0L)); + } +}