org.elasticsearch.common.lucene.uid.VersionsResolver.java Source code

Java tutorial

Introduction

Here is the source code for org.elasticsearch.common.lucene.uid.VersionsResolver.java

Source

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.elasticsearch.common.lucene.uid;

import org.apache.lucene.index.Fields;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReader.CoreClosedListener;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.NumericDocValues;
import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.index.SortedNumericDocValues;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.CloseableThreadLocal;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
import org.elasticsearch.index.mapper.SeqNoFieldMapper;
import org.elasticsearch.index.mapper.UidFieldMapper;
import org.elasticsearch.index.seqno.SequenceNumbersService;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.ConcurrentMap;

import static org.elasticsearch.common.lucene.uid.Versions.NOT_FOUND;

/** Utility class to resolve the Lucene doc ID and version for a given uid. */
public class VersionsResolver {

    static final ConcurrentMap<Object, CloseableThreadLocal<PerThreadIDAndVersionLookup>> lookupStates = ConcurrentCollections
            .newConcurrentMapWithAggressiveConcurrency();

    // Evict this reader from lookupStates once it's closed:
    private static final CoreClosedListener removeLookupState = key -> {
        CloseableThreadLocal<PerThreadIDAndVersionLookup> ctl = lookupStates.remove(key);
        if (ctl != null) {
            ctl.close();
        }
    };

    private static PerThreadIDAndVersionLookup getLookupState(LeafReader reader) throws IOException {
        Object key = reader.getCoreCacheKey();
        CloseableThreadLocal<PerThreadIDAndVersionLookup> ctl = lookupStates.get(key);
        if (ctl == null) {
            // First time we are seeing this reader's core; make a
            // new CTL:
            ctl = new CloseableThreadLocal<>();
            CloseableThreadLocal<PerThreadIDAndVersionLookup> other = lookupStates.putIfAbsent(key, ctl);
            if (other == null) {
                // Our CTL won, we must remove it when the
                // core is closed:
                reader.addCoreClosedListener(removeLookupState);
            } else {
                // Another thread beat us to it: just use
                // their CTL:
                ctl = other;
            }
        }

        PerThreadIDAndVersionLookup lookupState = ctl.get();
        if (lookupState == null) {
            lookupState = new PerThreadIDAndVersionLookup(reader);
            ctl.set(lookupState);
        }

        return lookupState;
    }

    private VersionsResolver() {
    }

    /**
     * Wraps an {@link LeafReaderContext}, a doc ID <b>relative to the context doc base</b> and
     * a version.
     **/
    public static class DocIdAndVersion {
        public final int docId;
        public final long version;
        public final LeafReaderContext context;

        public DocIdAndVersion(int docId, long version, LeafReaderContext context) {
            this.docId = docId;
            this.version = version;
            this.context = context;
        }
    }

    /**
     * Load the internal doc ID and version for the uid from the reader, returning<ul>
     * <li>null if the uid wasn't found,
     * <li>a doc ID and a version otherwise
     * </ul>
     */
    public static DocIdAndVersion loadDocIdAndVersion(IndexReader reader, Term term) throws IOException {
        assert term.field().equals(UidFieldMapper.NAME);
        List<LeafReaderContext> leaves = reader.leaves();
        if (leaves.isEmpty()) {
            return null;
        }
        // iterate backwards to optimize for the frequently updated documents
        // which are likely to be in the last segments
        for (int i = leaves.size() - 1; i >= 0; i--) {
            LeafReaderContext context = leaves.get(i);
            LeafReader leaf = context.reader();
            PerThreadIDAndVersionLookup lookup = getLookupState(leaf);
            DocIdAndVersion result = lookup.lookupVersion(term.bytes(), leaf.getLiveDocs(), context);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    /**
     * Load the version for the uid from the reader, returning<ul>
     * <li>{@link Versions#NOT_FOUND} if no matching doc exists,
     * <li>the version associated with the provided uid otherwise
     * </ul>
     */
    public static long loadVersion(IndexReader reader, Term term) throws IOException {
        final DocIdAndVersion docIdAndVersion = loadDocIdAndVersion(reader, term);
        return docIdAndVersion == null ? NOT_FOUND : docIdAndVersion.version;
    }

    /**
     * Returns the sequence number for the given uid term, returning
     * {@code SequenceNumbersService.UNASSIGNED_SEQ_NO} if none is found.
     */
    public static long loadSeqNo(IndexReader reader, Term term) throws IOException {
        assert term.field().equals(UidFieldMapper.NAME) : "can only load _seq_no by uid";
        List<LeafReaderContext> leaves = reader.leaves();
        if (leaves.isEmpty()) {
            return SequenceNumbersService.UNASSIGNED_SEQ_NO;
        }

        // iterate backwards to optimize for the frequently updated documents
        // which are likely to be in the last segments
        for (int i = leaves.size() - 1; i >= 0; i--) {
            LeafReader leaf = leaves.get(i).reader();
            Bits liveDocs = leaf.getLiveDocs();

            TermsEnum termsEnum = null;
            SortedNumericDocValues dvField = null;
            PostingsEnum docsEnum = null;

            final Fields fields = leaf.fields();
            if (fields != null) {
                Terms terms = fields.terms(UidFieldMapper.NAME);
                if (terms != null) {
                    termsEnum = terms.iterator();
                    assert termsEnum != null;
                    dvField = leaf.getSortedNumericDocValues(SeqNoFieldMapper.NAME);
                    assert dvField != null;

                    final BytesRef id = term.bytes();
                    if (termsEnum.seekExact(id)) {
                        // there may be more than one matching docID, in the
                        // case of nested docs, so we want the last one:
                        docsEnum = termsEnum.postings(docsEnum, 0);
                        int docID = DocIdSetIterator.NO_MORE_DOCS;
                        for (int d = docsEnum.nextDoc(); d != DocIdSetIterator.NO_MORE_DOCS; d = docsEnum
                                .nextDoc()) {
                            if (liveDocs != null && liveDocs.get(d) == false) {
                                continue;
                            }
                            docID = d;
                        }

                        if (docID != DocIdSetIterator.NO_MORE_DOCS) {
                            dvField.setDocument(docID);
                            assert dvField.count() == 1 : "expected only a single value for _seq_no but got "
                                    + dvField.count();
                            return dvField.valueAt(0);
                        }
                    }
                }
            }

        }
        return SequenceNumbersService.UNASSIGNED_SEQ_NO;
    }

    /**
     * Returns the primary term for the given uid term, returning {@code 0} if none is found.
     */
    public static long loadPrimaryTerm(IndexReader reader, Term term) throws IOException {
        assert term.field().equals(UidFieldMapper.NAME) : "can only load _primary_term by uid";
        List<LeafReaderContext> leaves = reader.leaves();
        if (leaves.isEmpty()) {
            return 0;
        }

        // iterate backwards to optimize for the frequently updated documents
        // which are likely to be in the last segments
        for (int i = leaves.size() - 1; i >= 0; i--) {
            LeafReader leaf = leaves.get(i).reader();
            Bits liveDocs = leaf.getLiveDocs();

            TermsEnum termsEnum = null;
            NumericDocValues dvField = null;
            PostingsEnum docsEnum = null;

            final Fields fields = leaf.fields();
            if (fields != null) {
                Terms terms = fields.terms(UidFieldMapper.NAME);
                if (terms != null) {
                    termsEnum = terms.iterator();
                    assert termsEnum != null;
                    dvField = leaf.getNumericDocValues(SeqNoFieldMapper.PRIMARY_TERM_NAME);
                    assert dvField != null;

                    final BytesRef id = term.bytes();
                    if (termsEnum.seekExact(id)) {
                        // there may be more than one matching docID, in the
                        // case of nested docs, so we want the last one:
                        docsEnum = termsEnum.postings(docsEnum, 0);
                        int docID = DocIdSetIterator.NO_MORE_DOCS;
                        for (int d = docsEnum.nextDoc(); d != DocIdSetIterator.NO_MORE_DOCS; d = docsEnum
                                .nextDoc()) {
                            if (liveDocs != null && liveDocs.get(d) == false) {
                                continue;
                            }
                            docID = d;
                        }

                        if (docID != DocIdSetIterator.NO_MORE_DOCS) {
                            return dvField.get(docID);
                        }
                    }
                }
            }

        }
        return 0;
    }
}