|  | @@ -11,9 +11,15 @@ import org.apache.lucene.util.BytesRef;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.breaker.CircuitBreaker;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.unit.ByteSizeValue;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.util.BigArrays;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.util.BitArray;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.util.BytesRefArray;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.util.DoubleArray;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.util.IntArray;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.util.LongArray;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.util.MockBigArrays;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.util.PageCacheRecycler;
 | 
	
		
			
				|  |  | +import org.elasticsearch.core.RefCounted;
 | 
	
		
			
				|  |  | +import org.elasticsearch.core.Releasable;
 | 
	
		
			
				|  |  |  import org.elasticsearch.core.Releasables;
 | 
	
		
			
				|  |  |  import org.elasticsearch.indices.breaker.CircuitBreakerService;
 | 
	
		
			
				|  |  |  import org.elasticsearch.test.ESTestCase;
 | 
	
	
		
			
				|  | @@ -1000,7 +1006,7 @@ public class BasicBlockTests extends ESTestCase {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      static void assertCannotDoubleRelease(Block block) {
 | 
	
		
			
				|  |  |          var ex = expectThrows(IllegalStateException.class, () -> block.close());
 | 
	
		
			
				|  |  | -        assertThat(ex.getMessage(), containsString("can't release already released block"));
 | 
	
		
			
				|  |  | +        assertThat(ex.getMessage(), containsString("can't release already released object"));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      static void assertCannotReadFromPage(Page page) {
 | 
	
	
		
			
				|  | @@ -1035,6 +1041,13 @@ public class BasicBlockTests extends ESTestCase {
 | 
	
		
			
				|  |  |          assertThat(breaker.getUsed(), is(0L));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    public void testRefCountingBigArrayBlock() {
 | 
	
		
			
				|  |  | +        Block block = randomBigArrayBlock();
 | 
	
		
			
				|  |  | +        assertThat(breaker.getUsed(), greaterThan(0L));
 | 
	
		
			
				|  |  | +        assertRefCountingBehavior(block);
 | 
	
		
			
				|  |  | +        assertThat(breaker.getUsed(), is(0L));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      public void testRefCountingConstantNullBlock() {
 | 
	
		
			
				|  |  |          Block block = blockFactory.newConstantNullBlock(10);
 | 
	
		
			
				|  |  |          assertThat(breaker.getUsed(), greaterThan(0L));
 | 
	
	
		
			
				|  | @@ -1051,52 +1064,165 @@ public class BasicBlockTests extends ESTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testRefCountingVectorBlock() {
 | 
	
		
			
				|  |  | -        Block block = randomNonDocVector().asBlock();
 | 
	
		
			
				|  |  | +        Block block = randomConstantVector().asBlock();
 | 
	
		
			
				|  |  |          assertThat(breaker.getUsed(), greaterThan(0L));
 | 
	
		
			
				|  |  |          assertRefCountingBehavior(block);
 | 
	
		
			
				|  |  |          assertThat(breaker.getUsed(), is(0L));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // Take a block with exactly 1 reference and assert that ref counting works fine.
 | 
	
		
			
				|  |  | -    static void assertRefCountingBehavior(Block b) {
 | 
	
		
			
				|  |  | -        assertTrue(b.hasReferences());
 | 
	
		
			
				|  |  | +    public void testRefCountingArrayVector() {
 | 
	
		
			
				|  |  | +        Vector vector = randomArrayVector();
 | 
	
		
			
				|  |  | +        assertThat(breaker.getUsed(), greaterThan(0L));
 | 
	
		
			
				|  |  | +        assertRefCountingBehavior(vector);
 | 
	
		
			
				|  |  | +        assertThat(breaker.getUsed(), is(0L));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public void testRefCountingBigArrayVector() {
 | 
	
		
			
				|  |  | +        Vector vector = randomBigArrayVector();
 | 
	
		
			
				|  |  | +        assertThat(breaker.getUsed(), greaterThan(0L));
 | 
	
		
			
				|  |  | +        assertRefCountingBehavior(vector);
 | 
	
		
			
				|  |  | +        assertThat(breaker.getUsed(), is(0L));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public void testRefCountingConstantVector() {
 | 
	
		
			
				|  |  | +        Vector vector = randomConstantVector();
 | 
	
		
			
				|  |  | +        assertThat(breaker.getUsed(), greaterThan(0L));
 | 
	
		
			
				|  |  | +        assertRefCountingBehavior(vector);
 | 
	
		
			
				|  |  | +        assertThat(breaker.getUsed(), is(0L));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public void testRefCountingDocVector() {
 | 
	
		
			
				|  |  | +        int positionCount = randomIntBetween(0, 100);
 | 
	
		
			
				|  |  | +        DocVector vector = new DocVector(intVector(positionCount), intVector(positionCount), intVector(positionCount), true);
 | 
	
		
			
				|  |  | +        assertThat(breaker.getUsed(), greaterThan(0L));
 | 
	
		
			
				|  |  | +        assertRefCountingBehavior(vector);
 | 
	
		
			
				|  |  | +        assertThat(breaker.getUsed(), is(0L));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * Take an object with exactly 1 reference and assert that ref counting works fine.
 | 
	
		
			
				|  |  | +     * Assumes that {@link Releasable#close()} and {@link RefCounted#decRef()} are equivalent.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    static <T extends RefCounted & Releasable> void assertRefCountingBehavior(T object) {
 | 
	
		
			
				|  |  | +        assertTrue(object.hasReferences());
 | 
	
		
			
				|  |  |          int numShallowCopies = randomIntBetween(0, 15);
 | 
	
		
			
				|  |  |          for (int i = 0; i < numShallowCopies; i++) {
 | 
	
		
			
				|  |  |              if (randomBoolean()) {
 | 
	
		
			
				|  |  | -                b.incRef();
 | 
	
		
			
				|  |  | +                object.incRef();
 | 
	
		
			
				|  |  |              } else {
 | 
	
		
			
				|  |  | -                assertTrue(b.tryIncRef());
 | 
	
		
			
				|  |  | +                assertTrue(object.tryIncRef());
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          for (int i = 0; i < numShallowCopies; i++) {
 | 
	
		
			
				|  |  |              if (randomBoolean()) {
 | 
	
		
			
				|  |  | -                b.close();
 | 
	
		
			
				|  |  | +                object.close();
 | 
	
		
			
				|  |  |              } else {
 | 
	
		
			
				|  |  |                  // closing and decRef'ing must be equivalent
 | 
	
		
			
				|  |  | -                assertFalse(b.decRef());
 | 
	
		
			
				|  |  | +                assertFalse(object.decRef());
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -            assertTrue(b.hasReferences());
 | 
	
		
			
				|  |  | +            assertTrue(object.hasReferences());
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          if (randomBoolean()) {
 | 
	
		
			
				|  |  | -            b.close();
 | 
	
		
			
				|  |  | +            object.close();
 | 
	
		
			
				|  |  |          } else {
 | 
	
		
			
				|  |  | -            assertTrue(b.decRef());
 | 
	
		
			
				|  |  | +            assertTrue(object.decRef());
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        assertFalse(b.hasReferences());
 | 
	
		
			
				|  |  | -        assertFalse(b.tryIncRef());
 | 
	
		
			
				|  |  | +        assertFalse(object.hasReferences());
 | 
	
		
			
				|  |  | +        assertFalse(object.tryIncRef());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        expectThrows(IllegalStateException.class, b::close);
 | 
	
		
			
				|  |  | -        expectThrows(IllegalStateException.class, b::incRef);
 | 
	
		
			
				|  |  | +        expectThrows(IllegalStateException.class, object::close);
 | 
	
		
			
				|  |  | +        expectThrows(IllegalStateException.class, object::incRef);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private IntVector intVector(int positionCount) {
 | 
	
		
			
				|  |  |          return blockFactory.newIntArrayVector(IntStream.range(0, positionCount).toArray(), positionCount);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    private Vector randomNonDocVector() {
 | 
	
		
			
				|  |  | +    private Vector randomArrayVector() {
 | 
	
		
			
				|  |  | +        int positionCount = randomIntBetween(0, 100);
 | 
	
		
			
				|  |  | +        int vectorType = randomIntBetween(0, 4);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return switch (vectorType) {
 | 
	
		
			
				|  |  | +            case 0 -> {
 | 
	
		
			
				|  |  | +                boolean[] values = new boolean[positionCount];
 | 
	
		
			
				|  |  | +                Arrays.fill(values, randomBoolean());
 | 
	
		
			
				|  |  | +                yield blockFactory.newBooleanArrayVector(values, positionCount);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            case 1 -> {
 | 
	
		
			
				|  |  | +                BytesRefArray values = new BytesRefArray(positionCount, BigArrays.NON_RECYCLING_INSTANCE);
 | 
	
		
			
				|  |  | +                for (int i = 0; i < positionCount; i++) {
 | 
	
		
			
				|  |  | +                    values.append(new BytesRef(randomByteArrayOfLength(between(1, 20))));
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield blockFactory.newBytesRefArrayVector(values, positionCount);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            case 2 -> {
 | 
	
		
			
				|  |  | +                double[] values = new double[positionCount];
 | 
	
		
			
				|  |  | +                Arrays.fill(values, 1.0);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield blockFactory.newDoubleArrayVector(values, positionCount);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            case 3 -> {
 | 
	
		
			
				|  |  | +                int[] values = new int[positionCount];
 | 
	
		
			
				|  |  | +                Arrays.fill(values, 1);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield blockFactory.newIntArrayVector(values, positionCount);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            default -> {
 | 
	
		
			
				|  |  | +                long[] values = new long[positionCount];
 | 
	
		
			
				|  |  | +                Arrays.fill(values, 1L);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield blockFactory.newLongArrayVector(values, positionCount);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private Vector randomBigArrayVector() {
 | 
	
		
			
				|  |  | +        int positionCount = randomIntBetween(0, 10000);
 | 
	
		
			
				|  |  | +        int arrayType = randomIntBetween(0, 3);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return switch (arrayType) {
 | 
	
		
			
				|  |  | +            case 0 -> {
 | 
	
		
			
				|  |  | +                BitArray values = new BitArray(positionCount, blockFactory.bigArrays());
 | 
	
		
			
				|  |  | +                for (int i = 0; i < positionCount; i++) {
 | 
	
		
			
				|  |  | +                    if (randomBoolean()) {
 | 
	
		
			
				|  |  | +                        values.set(positionCount);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield new BooleanBigArrayVector(values, positionCount, blockFactory);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            case 1 -> {
 | 
	
		
			
				|  |  | +                DoubleArray values = blockFactory.bigArrays().newDoubleArray(positionCount, false);
 | 
	
		
			
				|  |  | +                for (int i = 0; i < positionCount; i++) {
 | 
	
		
			
				|  |  | +                    values.set(i, randomDouble());
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield new DoubleBigArrayVector(values, positionCount, blockFactory);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            case 2 -> {
 | 
	
		
			
				|  |  | +                IntArray values = blockFactory.bigArrays().newIntArray(positionCount, false);
 | 
	
		
			
				|  |  | +                for (int i = 0; i < positionCount; i++) {
 | 
	
		
			
				|  |  | +                    values.set(i, randomInt());
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield new IntBigArrayVector(values, positionCount, blockFactory);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            default -> {
 | 
	
		
			
				|  |  | +                LongArray values = blockFactory.bigArrays().newLongArray(positionCount, false);
 | 
	
		
			
				|  |  | +                for (int i = 0; i < positionCount; i++) {
 | 
	
		
			
				|  |  | +                    values.set(i, randomLong());
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield new LongBigArrayVector(values, positionCount, blockFactory);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private Vector randomConstantVector() {
 | 
	
		
			
				|  |  |          int positionCount = randomIntBetween(0, 100);
 | 
	
		
			
				|  |  |          int vectorType = randomIntBetween(0, 4);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1116,7 +1242,7 @@ public class BasicBlockTests extends ESTestCase {
 | 
	
		
			
				|  |  |          return switch (arrayType) {
 | 
	
		
			
				|  |  |              case 0 -> {
 | 
	
		
			
				|  |  |                  boolean[] values = new boolean[positionCount];
 | 
	
		
			
				|  |  | -                Arrays.fill(values, true);
 | 
	
		
			
				|  |  | +                Arrays.fill(values, randomBoolean());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  yield blockFactory.newBooleanArrayBlock(values, positionCount, new int[] {}, new BitSet(), randomOrdering());
 | 
	
		
			
				|  |  |              }
 | 
	
	
		
			
				|  | @@ -1148,4 +1274,46 @@ public class BasicBlockTests extends ESTestCase {
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          };
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private Block randomBigArrayBlock() {
 | 
	
		
			
				|  |  | +        int positionCount = randomIntBetween(0, 10000);
 | 
	
		
			
				|  |  | +        int arrayType = randomIntBetween(0, 3);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return switch (arrayType) {
 | 
	
		
			
				|  |  | +            case 0 -> {
 | 
	
		
			
				|  |  | +                BitArray values = new BitArray(positionCount, blockFactory.bigArrays());
 | 
	
		
			
				|  |  | +                for (int i = 0; i < positionCount; i++) {
 | 
	
		
			
				|  |  | +                    if (randomBoolean()) {
 | 
	
		
			
				|  |  | +                        values.set(positionCount);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield new BooleanBigArrayBlock(values, positionCount, null, new BitSet(), Block.MvOrdering.UNORDERED, blockFactory);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            case 1 -> {
 | 
	
		
			
				|  |  | +                DoubleArray values = blockFactory.bigArrays().newDoubleArray(positionCount, false);
 | 
	
		
			
				|  |  | +                for (int i = 0; i < positionCount; i++) {
 | 
	
		
			
				|  |  | +                    values.set(i, randomDouble());
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield new DoubleBigArrayBlock(values, positionCount, null, new BitSet(), Block.MvOrdering.UNORDERED, blockFactory);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            case 2 -> {
 | 
	
		
			
				|  |  | +                IntArray values = blockFactory.bigArrays().newIntArray(positionCount, false);
 | 
	
		
			
				|  |  | +                for (int i = 0; i < positionCount; i++) {
 | 
	
		
			
				|  |  | +                    values.set(i, randomInt());
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield new IntBigArrayBlock(values, positionCount, null, new BitSet(), Block.MvOrdering.UNORDERED, blockFactory);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            default -> {
 | 
	
		
			
				|  |  | +                LongArray values = blockFactory.bigArrays().newLongArray(positionCount, false);
 | 
	
		
			
				|  |  | +                for (int i = 0; i < positionCount; i++) {
 | 
	
		
			
				|  |  | +                    values.set(i, randomLong());
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                yield new LongBigArrayBlock(values, positionCount, null, new BitSet(), Block.MvOrdering.UNORDERED, blockFactory);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  }
 |