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

com.hazelcast.cache.impl.journal.RingbufferCacheEventJournalImpl Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show newest version
/*
 * Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved.
 *
 * 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.hazelcast.cache.impl.journal;

import com.hazelcast.cache.CacheEventType;
import com.hazelcast.cache.CacheNotExistsException;
import com.hazelcast.cache.impl.CacheService;
import com.hazelcast.config.CacheConfig;
import com.hazelcast.config.EventJournalConfig;
import com.hazelcast.config.InMemoryFormat;
import com.hazelcast.config.RingbufferConfig;
import com.hazelcast.internal.serialization.InternalSerializationService;
import com.hazelcast.logging.ILogger;
import com.hazelcast.internal.serialization.Data;
import com.hazelcast.internal.serialization.DataType;
import com.hazelcast.ringbuffer.impl.ReadResultSetImpl;
import com.hazelcast.ringbuffer.impl.RingbufferContainer;
import com.hazelcast.ringbuffer.impl.RingbufferService;
import com.hazelcast.ringbuffer.impl.RingbufferWaitNotifyKey;
import com.hazelcast.spi.impl.NodeEngine;
import com.hazelcast.internal.services.ObjectNamespace;
import com.hazelcast.spi.impl.NodeEngineImpl;
import com.hazelcast.spi.impl.operationparker.OperationParker;
import com.hazelcast.spi.impl.operationservice.WaitNotifyKey;

import static com.hazelcast.cache.CacheEventType.CREATED;
import static com.hazelcast.cache.CacheEventType.EVICTED;
import static com.hazelcast.cache.CacheEventType.EXPIRED;
import static com.hazelcast.cache.CacheEventType.REMOVED;
import static com.hazelcast.cache.CacheEventType.UPDATED;
import static java.lang.String.format;


/**
 * The cache event journal implementation based on the {@link com.hazelcast.ringbuffer.Ringbuffer}.
 * It will add all journal events into a {@link RingbufferContainer} with the provided namespace
 * and partition ID and allows checking if the cache has a configured event journal.
 * Adapts the {@link EventJournalConfig} to the {@link RingbufferConfig} when creating the ringbuffer.
 */
public class RingbufferCacheEventJournalImpl implements CacheEventJournal {

    private final NodeEngineImpl nodeEngine;
    private final ILogger logger;

    public RingbufferCacheEventJournalImpl(NodeEngine engine) {
        this.nodeEngine = (NodeEngineImpl) engine;
        this.logger = this.nodeEngine.getLogger(RingbufferCacheEventJournalImpl.class);
    }

    @Override
    public void writeUpdateEvent(EventJournalConfig journalConfig, ObjectNamespace namespace, int partitionId,
                                 Data key, Object oldValue, Object newValue) {
        addToEventRingbuffer(journalConfig, namespace, partitionId, UPDATED, key, oldValue, newValue);
    }

    @Override
    public void writeCreatedEvent(EventJournalConfig journalConfig, ObjectNamespace namespace, int partitionId,
                                  Data key, Object value) {
        addToEventRingbuffer(journalConfig, namespace, partitionId, CREATED, key, null, value);
    }

    @Override
    public void writeRemoveEvent(EventJournalConfig journalConfig, ObjectNamespace namespace, int partitionId,
                                 Data key, Object value) {
        addToEventRingbuffer(journalConfig, namespace, partitionId, REMOVED, key, value, null);
    }

    @Override
    public void writeEvictEvent(EventJournalConfig journalConfig, ObjectNamespace namespace, int partitionId,
                                Data key, Object value) {
        addToEventRingbuffer(journalConfig, namespace, partitionId, EVICTED, key, value, null);
    }

    @Override
    public void writeExpiredEvent(EventJournalConfig journalConfig, ObjectNamespace namespace, int partitionId,
                                  Data key, Object value) {
        addToEventRingbuffer(journalConfig, namespace, partitionId, EXPIRED, key, value, null);
    }

    @Override
    public long newestSequence(ObjectNamespace namespace, int partitionId) {
        return getRingbufferOrFail(namespace, partitionId).tailSequence();
    }

    @Override
    public long oldestSequence(ObjectNamespace namespace, int partitionId) {
        return getRingbufferOrFail(namespace, partitionId).headSequence();
    }

    @Override
    public boolean isPersistenceEnabled(ObjectNamespace namespace, int partitionId) {
        return getRingbufferOrFail(namespace, partitionId).getStore().isEnabled();
    }

    @Override
    public void destroy(ObjectNamespace namespace, int partitionId) {
        RingbufferService service;
        try {
            service = getRingbufferService();
        } catch (Exception e) {
            if (nodeEngine.isRunning()) {
                logger.fine("Could not retrieve ringbuffer service to destroy event journal " + namespace, e);
            }
            return;
        }
        service.destroyContainer(partitionId, namespace);
    }

    @Override
    public void isAvailableOrNextSequence(ObjectNamespace namespace, int partitionId, long sequence) {
        getRingbufferOrFail(namespace, partitionId).checkBlockableReadSequence(sequence);
    }

    @Override
    public boolean isNextAvailableSequence(ObjectNamespace namespace, int partitionId, long sequence) {
        return getRingbufferOrFail(namespace, partitionId).shouldWait(sequence);
    }

    @Override
    public WaitNotifyKey getWaitNotifyKey(ObjectNamespace namespace, int partitionId) {
        return new RingbufferWaitNotifyKey(namespace, partitionId);
    }

    @Override
    public  long readMany(ObjectNamespace namespace, int partitionId, long beginSequence,
                             ReadResultSetImpl resultSet) {
        return getRingbufferOrFail(namespace, partitionId).readMany(beginSequence, resultSet);
    }

    @Override
    public void cleanup(ObjectNamespace namespace, int partitionId) {
        getRingbufferOrFail(namespace, partitionId).cleanup();
    }

    /**
     * {@inheritDoc}
     * NOTE: The cache config should have already been created in the cache service before
     * invoking this method.
     *
     * @param namespace the cache namespace, containing the full prefixed cache name
     * @return {@code true} if the object has a configured and enabled event journal, {@code false} otherwise
     * @throws CacheNotExistsException if the cache configuration was not found
     */
    @Override
    public boolean hasEventJournal(ObjectNamespace namespace) {
        return getEventJournalConfig(namespace) != null;
    }

    /**
     * {@inheritDoc}
     *
     * @throws CacheNotExistsException if the cache configuration was not found
     */
    @Override
    public EventJournalConfig getEventJournalConfig(ObjectNamespace namespace) {
        String name = namespace.getObjectName();
        CacheConfig cacheConfig = getCacheService().getCacheConfig(name);
        if (cacheConfig == null) {
            throw new CacheNotExistsException("Cache " + name + " is already destroyed or not created yet, on "
                    + nodeEngine.getLocalMember());
        }
        EventJournalConfig config = cacheConfig.getEventJournalConfig();
        if (config == null || !config.isEnabled()) {
            return null;
        }
        return config;
    }

    /**
     * {@inheritDoc}
     *
     * @throws CacheNotExistsException if the cache configuration was not found
     */
    @Override
    public RingbufferConfig toRingbufferConfig(EventJournalConfig config, ObjectNamespace namespace) {
        CacheConfig cacheConfig = getCacheService().getCacheConfig(namespace.getObjectName());
        if (cacheConfig == null) {
            throw new CacheNotExistsException("Cache " + namespace.getObjectName()
                    + " is already destroyed or not created yet, on "
                    + nodeEngine.getLocalMember());
        }
        int partitionCount = nodeEngine.getPartitionService().getPartitionCount();
        return new RingbufferConfig()
                .setAsyncBackupCount(cacheConfig.getAsyncBackupCount())
                .setBackupCount(cacheConfig.getBackupCount())
                .setInMemoryFormat(InMemoryFormat.OBJECT)
                .setCapacity(config.getCapacity() / partitionCount)
                .setTimeToLiveSeconds(config.getTimeToLiveSeconds());
    }

    private void addToEventRingbuffer(EventJournalConfig journalConfig, ObjectNamespace namespace, int partitionId,
                                      CacheEventType eventType, Data key, Object oldValue, Object newValue) {
        if (journalConfig == null || !journalConfig.isEnabled()) {
            return;
        }
        RingbufferContainer eventContainer
                = getRingbufferOrNull(journalConfig, namespace, partitionId);
        if (eventContainer == null) {
            return;
        }
        InternalEventJournalCacheEvent event
                = new InternalEventJournalCacheEvent(toData(key), toData(newValue), toData(oldValue), eventType.getType());
        eventContainer.add(event);
        getOperationParker().unpark(eventContainer);
    }

    protected Data toData(Object val) {
        return getSerializationService().toData(val, DataType.HEAP);
    }

    /**
     * Gets or creates a ringbuffer for an event journal or throws an exception if no
     * event journal is configured. The caller should call
     * {@link #hasEventJournal(ObjectNamespace)} before invoking this method to avoid getting
     * the exception. This method can be used to get the ringbuffer on which future events
     * will be added once the cache has been created.
     * 

* NOTE: The cache config should have already been created in the cache service before * invoking this method. * * @param namespace the cache namespace, containing the full prefixed cache name * @param partitionId the cache partition ID * @return the cache partition event journal * @throws CacheNotExistsException if the cache configuration was not found * @throws IllegalStateException if there is no event journal configured for this cache */ private RingbufferContainer getRingbufferOrFail(ObjectNamespace namespace, int partitionId) { RingbufferService ringbufferService = getRingbufferService(); RingbufferContainer container = ringbufferService.getContainerOrNull(partitionId, namespace); if (container != null) { return container; } EventJournalConfig config = getEventJournalConfig(namespace); if (config == null) { throw new IllegalStateException(format( "There is no event journal configured for cache %s or the journal is disabled", namespace.getObjectName())); } return getOrCreateRingbufferContainer(namespace, partitionId, config); } /** * Gets or creates a ringbuffer for an event journal or returns {@link null} if no * event journal is configured, it is disabled or not available. * * @param journalConfig the event journal configuration for this specific cache * @param namespace the cache namespace, containing the full prefixed cache name * @param partitionId the cache partition ID * @return the cache partition event journal or {@code null} if no journal is configured for this cache * @throws CacheNotExistsException if the cache configuration was not found * @see #getEventJournalConfig(ObjectNamespace) */ private RingbufferContainer getRingbufferOrNull( EventJournalConfig journalConfig, ObjectNamespace namespace, int partitionId) { RingbufferService ringbufferService = getRingbufferService(); RingbufferContainer container = ringbufferService.getContainerOrNull(partitionId, namespace); if (container != null) { return container; } return journalConfig != null ? getOrCreateRingbufferContainer(namespace, partitionId, journalConfig) : null; } private RingbufferContainer getOrCreateRingbufferContainer( ObjectNamespace namespace, int partitionId, EventJournalConfig config) { RingbufferConfig ringbufferConfig = toRingbufferConfig(config, namespace); return getRingbufferService().getOrCreateContainer(partitionId, namespace, ringbufferConfig); } private RingbufferService getRingbufferService() { return nodeEngine.getService(RingbufferService.SERVICE_NAME); } private OperationParker getOperationParker() { return nodeEngine.getOperationParker(); } private InternalSerializationService getSerializationService() { return (InternalSerializationService) nodeEngine.getSerializationService(); } private CacheService getCacheService() { return nodeEngine.getService(CacheService.SERVICE_NAME); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy