Package org.apache.jackrabbit.oak.plugins.segment.mongo

Source Code of org.apache.jackrabbit.oak.plugins.segment.mongo.MongoJournal

/*
* 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.segment.mongo;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableMap.of;
import static com.mongodb.ReadPreference.nearest;
import static com.mongodb.ReadPreference.primaryPreferred;

import java.util.concurrent.TimeUnit;

import org.apache.jackrabbit.oak.plugins.segment.Journal;
import org.apache.jackrabbit.oak.plugins.segment.MergeDiff;
import org.apache.jackrabbit.oak.plugins.segment.RecordId;
import org.apache.jackrabbit.oak.plugins.segment.Segment;
import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeState;
import org.apache.jackrabbit.oak.plugins.segment.SegmentStore;
import org.apache.jackrabbit.oak.plugins.segment.SegmentWriter;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;

import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import com.mongodb.WriteConcern;
import com.mongodb.WriteResult;

class MongoJournal implements Journal {

    private static final long UPDATE_INTERVAL =
            TimeUnit.NANOSECONDS.convert(10, TimeUnit.MILLISECONDS);

    private final SegmentStore store;

    private final DBCollection journals;

    private final WriteConcern concern;

    private final String name;

    private DBObject state;

    private long stateLastUpdated;

    MongoJournal(
            SegmentStore store, DBCollection journals,
            WriteConcern concern, NodeState head) {
        this.store = checkNotNull(store);
        this.journals = checkNotNull(journals);
        this.concern = checkNotNull(concern);
        this.name = "root";

        DBObject id = new BasicDBObject("_id", "root");
        state = journals.findOne(id, null, primaryPreferred());
        if (state == null) {
            SegmentWriter writer = store.getWriter();
            RecordId headId = writer.writeNode(head).getRecordId();
            writer.flush();
            state = new BasicDBObject(of(
                    "_id""root",
                    "head", headId.toString()));
            try {
                journals.insert(state, concern);
            } catch (MongoException.DuplicateKey e) {
                // Someone else managed to concurrently create the journal,
                // so let's just re-read it from the database
                state = journals.findOne(id, null, primaryPreferred());
                checkState(state != null);
            }
        }
        stateLastUpdated = System.nanoTime();
    }

    MongoJournal(
            SegmentStore store, DBCollection journals,
            WriteConcern concern, String name) {
        this.store = checkNotNull(store);
        this.journals = checkNotNull(journals);
        this.concern = checkNotNull(concern);
        this.name = checkNotNull(name);
        checkArgument(!"root".equals(name));

        DBObject id = new BasicDBObject("_id", name);
        state = journals.findOne(id, null, primaryPreferred());
        if (state == null) {
            Journal root = store.getJournal("root");
            String head = root.getHead().toString();
            state = new BasicDBObject(of(
                    "_id",    name,
                    "parent", "root",
                    "base",   head,
                    "head",   head));
            try {
                journals.insert(state, concern);
            } catch (MongoException.DuplicateKey e) {
                // Someone else managed to concurrently create the journal,
                // so let's just re-read it from the database
                state = journals.findOne(id, null, primaryPreferred());
                checkState(state != null);
            }
        }
        stateLastUpdated = System.nanoTime();
    }

    @Override
    public RecordId getHead() {
        long now = System.nanoTime();
        if (stateLastUpdated + UPDATE_INTERVAL < now) {
            DBObject id = new BasicDBObject("_id", name);
            DBObject freshState = journals.findOne(id, null, nearest());
            if (freshState == null) {
                freshState = journals.findOne(id, null, primaryPreferred());
                checkState(freshState != null);
            }
            state = freshState;
            stateLastUpdated = now;
        }
        return RecordId.fromString(state.get("head").toString());
    }

    @Override
    public synchronized boolean setHead(RecordId base, RecordId head) {
        if (!base.equals(getHead())) {
            return false;
        }

        // Flush any pending records
        store.getWriter().flush();

        BasicDBObjectBuilder builder = BasicDBObjectBuilder.start();
        builder.add("_id", name);
        if (state.containsField("parent")) {
            builder.add("parent", state.get("parent"));
        }
        if (state.containsField("base")) {
            builder.add("base", state.get("base"));
        }
        builder.add("head", head.toString());
        DBObject nextState = builder.get();

        WriteResult result =
                journals.update(state, nextState, false, false, concern);
        if (result.getN() == 1) {
            state = nextState;
            stateLastUpdated = System.nanoTime();
            return true;
        } else {
            // force refresh when next accessed
            stateLastUpdated -= UPDATE_INTERVAL;
            return false;
        }
    }

    @Override
    public synchronized void merge() {
        DBObject id = new BasicDBObject("_id", name);
        DBObject state = journals.findOne(id, null, primaryPreferred());
        checkState(state != null);

        if (state.containsField("parent")) {
            RecordId base = RecordId.fromString(state.get("base").toString());
            RecordId head = RecordId.fromString(state.get("head").toString());

            SegmentWriter writer = store.getWriter();
            Segment segment = writer.getDummySegment();

            NodeState before = new SegmentNodeState(segment, base);
            NodeState after = new SegmentNodeState(segment, head);

            Journal parent = store.getJournal(state.get("parent").toString());
            while (!parent.setHead(base, head)) {
                RecordId newBase = parent.getHead();
                NodeBuilder builder =
                        new SegmentNodeState(segment, newBase).builder();
                after.compareAgainstBaseState(before, new MergeDiff(builder));
                RecordId newHead =
                        writer.writeNode(builder.getNodeState()).getRecordId();

                base = newBase;
                head = newHead;
            }

            base = head;

            BasicDBObjectBuilder builder = BasicDBObjectBuilder.start();
            builder.add("_id", name);
            builder.add("parent", state.get("parent"));
            builder.add("base", base.toString());
            builder.add("head", head.toString());
            // TODO: concurrent updates?
            journals.update(state, builder.get(), false, false, concern);
        }
    }

}
TOP

Related Classes of org.apache.jackrabbit.oak.plugins.segment.mongo.MongoJournal

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.