Package org.apache.jackrabbit.oak.plugins.index

Source Code of org.apache.jackrabbit.oak.plugins.index.AsyncIndexUpdate

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF 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.apache.jackrabbit.oak.plugins.index;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.apache.jackrabbit.oak.api.Type.STRING;
import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;

import java.util.concurrent.TimeUnit;

import javax.annotation.Nonnull;

import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.spi.commit.CommitHook;
import org.apache.jackrabbit.oak.spi.commit.EditorDiff;
import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
import org.apache.jackrabbit.oak.spi.commit.PostCommitHook;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Objects;

public class AsyncIndexUpdate implements Runnable {

    private static final Logger log = LoggerFactory
            .getLogger(AsyncIndexUpdate.class);

    /**
     * Name of the hidden node under which information about the checkpoints
     * seen and indexed by each async indexer is kept.
     */
    private static final String ASYNC = ":async";

    private static final long DEFAULT_LIFETIME = TimeUnit.HOURS.toMillis(1);

    private static final CommitFailedException CONCURRENT_UPDATE = new CommitFailedException(
            "Async", 1, "Concurrent update detected");

    private final String name;

    private final NodeStore store;

    private final IndexEditorProvider provider;

    private final long lifetime = DEFAULT_LIFETIME; // TODO: make configurable

    /** Flag to avoid repeatedly logging failure warnings */
    private boolean failing = false;

    public AsyncIndexUpdate(@Nonnull String name, @Nonnull NodeStore store,
            @Nonnull IndexEditorProvider provider) {
        this.name = checkNotNull(name);
        this.store = checkNotNull(store);
        this.provider = checkNotNull(provider);
    }

    @Override
    public synchronized void run() {
        log.debug("Running background index task {}", name);
        String checkpoint = store.checkpoint(lifetime);
        NodeState after = store.retrieve(checkpoint);
        if (after == null) {
            log.debug("Unable to retrieve checkpoint {}", checkpoint);
            return;
        }

        preAsyncRun(store);

        NodeStoreBranch branch = store.branch();
        NodeBuilder builder = branch.getHead().builder();
        NodeBuilder async = builder.child(ASYNC);

        NodeState before = null;
        final PropertyState state = async.getProperty(name);
        if (state != null && state.getType() == STRING) {
            before = store.retrieve(state.getValue(STRING));
        }
        if (before == null) {
            before = MISSING_NODE;
        }

        CommitFailedException exception = EditorDiff.process(new IndexUpdate(
                provider, name, after, builder), before, after);
        if (exception == null) {
            try {
                async.setProperty(name, checkpoint);
                postAsyncRunStatus(builder);
                branch.setRoot(builder.getNodeState());
                branch.merge(new CommitHook() {
                    @Override
                    @Nonnull
                    public NodeState processCommit(NodeState before,
                            NodeState after) throws CommitFailedException {
                        // check for concurrent updates by this async task
                        PropertyState stateAfterRebase = before.getChildNode(
                                ASYNC).getProperty(name);
                        if (Objects.equal(state, stateAfterRebase)) {
                            return after;
                        } else {
                            throw CONCURRENT_UPDATE;
                        }
                    }
                }, PostCommitHook.EMPTY);
            } catch (CommitFailedException e) {
                if (e != CONCURRENT_UPDATE) {
                    exception = e;
                }
            }
        }

        if (exception != null) {
            if (!failing) {
                log.warn("Index update {} failed", name, exception);
            }
            failing = true;
        } else {
            if (failing) {
                log.info("Index update {} no longer fails", name);
            }
            failing = false;
        }
    }

    private void preAsyncRun(NodeStore store) {
        NodeStoreBranch branch = store.branch();
        NodeBuilder builder = branch.getHead().builder();
        preAsyncRunStatus(builder);
        branch.setRoot(builder.getNodeState());
        try {
            branch.merge(EmptyHook.INSTANCE, PostCommitHook.EMPTY);
        } catch (CommitFailedException e) {
            log.warn("Index status update {} failed", name, e);
        }
    }

    private static void preAsyncRunStatus(NodeBuilder builder) {
        builder.getChildNode(IndexConstants.INDEX_DEFINITIONS_NAME)
                .setProperty("async-status", "running")
                .setProperty("async-start", System.currentTimeMillis(),
                        Type.DATE).removeProperty("async-done");
    }

    private static void postAsyncRunStatus(NodeBuilder builder) {
        builder.getChildNode(IndexConstants.INDEX_DEFINITIONS_NAME)
                .setProperty("async-status", "done")
                .setProperty("async-done", System.currentTimeMillis(),
                        Type.DATE).removeProperty("async-start");
    }
}
TOP

Related Classes of org.apache.jackrabbit.oak.plugins.index.AsyncIndexUpdate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.