Currently if you provide an ANN search using the `knn` search section, the search doesn't respect cancellation. This is because ANN searches use their own Lucene data structures that aren't covered by `ExitableDirectoryReader`.

This class is forked from Lucene, and in Lucene's version we recently added fine-grained cancellation for ANN search (https://github.com/apache/lucene/pull/833). So I think we just need to fold in the changes to the Elasticsearch version.
server/src/test/java/org/elasticsearch/search/SearchCancellationTests.java
import org.apache.lucene.store.Directory;
import org.apache.lucene.tests.index.RandomIndexWriter;
import org.apache.lucene.tests.util.TestUtil;
import org.apache.lucene.util.automaton.CompiledAutomaton;
import org.apache.lucene.util.automaton.RegExp;
import org.elasticsearch.core.IOUtils;
import org.elasticsearch.search.internal.ContextIndexSearcher;
import org.elasticsearch.tasks.TaskCancelledException;
import org.elasticsearch.test.ESTestCase;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.hamcrest.Matchers.equalTo;
public class SearchCancellationTests extends ESTestCase {
    private static final String STRING_FIELD_NAME = "foo";
    private static final String POINT_FIELD_NAME = "point";
    private static Directory dir;
    private static IndexReader reader;
    @BeforeClass
    public static void setup() throws IOException {
        dir = newDirectory();
        RandomIndexWriter w = new RandomIndexWriter(random(), dir);
        
        w.w.getConfig().setMergePolicy(NoMergePolicy.INSTANCE);
        w.setDoRandomForceMerge(false);
        indexRandomDocuments(w, TestUtil.nextInt(random(), 2, 20));
        w.flush();
        indexRandomDocuments(w, TestUtil.nextInt(random(), 1, 20));
        reader = w.getReader();