All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.apple.foundationdb.record.MutableRecordStoreState Maven / Gradle / Ivy

There is a newer version: 2.8.110.0
Show newest version
/*
 * MutableRecordStoreState.java
 *
 * This source file is part of the FoundationDB open source project
 *
 * Copyright 2015-2018 Apple Inc. and the FoundationDB project authors
 *
 * Licensed 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 com.apple.foundationdb.record;

import com.apple.foundationdb.annotation.API;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * A record store that can be modified to reflect changes made to the database.
 * Such modifications are only allowed between {@link #beginWrite} and {@link #endWrite} and will conflict with
 * {@link #beginRead} ... {@link #endRead}.
 */
@API(API.Status.INTERNAL)
public class MutableRecordStoreState extends RecordStoreState {

    private static final long READ_MASK = 0xFFFF0000;
    private static final long WRITE_MASK = 0x0000FFFF;

    private final AtomicLong users = new AtomicLong();

    public MutableRecordStoreState(@Nullable RecordMetaDataProto.DataStoreInfo storeHeader, @Nullable Map indexStateMap) {
        super(storeHeader, indexStateMap);
    }

    // Copy constructor
    MutableRecordStoreState(@Nonnull RecordStoreState recordStoreState) {
        this(recordStoreState.getStoreHeader(), recordStoreState.getIndexStates());
    }

    @Deprecated
    public MutableRecordStoreState(@Nullable Map indexStateMap) {
        super(indexStateMap);
    }

    private static long readIncrement(long u) {
        return ((((u >> 32) + 1) << 32) & READ_MASK) | (u & WRITE_MASK);
    }

    private static long readDecrement(long u) {
        return ((((u >> 32) - 1) << 32) & READ_MASK) | (u & WRITE_MASK);
    }

    private static long writeIncrement(long u) {
        return (u & READ_MASK) | (((u & WRITE_MASK) + 1) & WRITE_MASK);
    }

    private static long writeDecrement(long u) {
        return (u & READ_MASK) | (((u & WRITE_MASK) - 1) & WRITE_MASK);
    }

    private void verifyWritable() {
        if ((users.get() & WRITE_MASK) == 0) {
            throw new RecordCoreException("record store state is not enabled for modification");
        }
    }

    /**
     * Begin using this record store state.
     * Until {@link #endRead} is called, the state will not change.
     */
    @Override
    public void beginRead() {
        long inuse = users.updateAndGet(MutableRecordStoreState::readIncrement);
        if ((inuse & WRITE_MASK) != 0) {
            users.updateAndGet(MutableRecordStoreState::readDecrement);
            throw new RecordCoreException("record store state is being modified");
        }
    }

    /**
     * End using this record store state.
     * @see #beginRead()
     */
    @Override
    public void endRead() {
        users.updateAndGet(MutableRecordStoreState::readDecrement);
    }

    /**
     * Begin modifying this record store state.
     * Until {@link #endWrite} is called, the state will not change.
     */
    public void beginWrite() {
        // One-time transition to mutable map inside.
        Map current = indexStateMap.get();
        if (!(current instanceof ConcurrentHashMap)) {
            indexStateMap.compareAndSet(current, new ConcurrentHashMap<>(current));
        }

        long inuse = users.updateAndGet(MutableRecordStoreState::writeIncrement);
        if ((inuse & READ_MASK) != 0) {
            users.updateAndGet(MutableRecordStoreState::writeDecrement);
            throw new RecordCoreException("record store state is being used for queries");
        }
    }

    /**
     * End modifying this record store state.
     * @see #beginWrite()
     */
    public void endWrite() {
        users.updateAndGet(MutableRecordStoreState::writeDecrement);
    }

    /**
     * Modify the state of an index in this map. The caller should modify the database to match.
     *
     * @param indexName the index name
     * @param state the new state for the given index
     * @return the previous state of the given index
     */
    @Nonnull
    public IndexState setState(@Nonnull String indexName, @Nonnull IndexState state) {
        verifyWritable();
        IndexState previous;
        if (state == IndexState.READABLE) {
            previous = indexStateMap.get().remove(indexName);
        } else {
            previous = indexStateMap.get().put(indexName, state);
        }
        return previous == null ? IndexState.READABLE : previous;
    }

    @Nonnull
    @Override
    public MutableRecordStoreState withWriteOnlyIndexes(@Nonnull final List writeOnlyIndexNames) {
        return new MutableRecordStoreState(getStoreHeader(), writeOnlyMap(writeOnlyIndexNames));
    }

    /**
     * Update the store header in this record store state. The caller should modify the state in the database
     * to match.
     *
     * @param storeHeader the updated store header
     * @return the previous store header value
     */
    @Nonnull
    public RecordMetaDataProto.DataStoreInfo setStoreHeader(@Nonnull RecordMetaDataProto.DataStoreInfo storeHeader) {
        verifyWritable();
        return this.storeHeader.getAndSet(storeHeader);
    }

    // NOTE: Does not actually implement own equals. Is equal to immutable one, too.
    @Override
    @SuppressWarnings("PMD.UselessOverridingMethod")
    public boolean equals(Object o) {
        return super.equals(o);
    }

    @Override
    @SuppressWarnings("PMD.UselessOverridingMethod")
    public int hashCode() {
        return super.hashCode();
    }

    @Nonnull
    @Override
    public RecordStoreState toImmutable() {
        return new RecordStoreState(storeHeader.get(), indexStateMap.get());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy