package com.jivesoftware.os.amza.service.replication; import com.jivesoftware.os.amza.api.filer.UIO; import com.jivesoftware.os.amza.api.partition.PartitionName; import com.jivesoftware.os.amza.api.scan.RowsChanged; import com.jivesoftware.os.amza.api.wal.WALUpdated; import com.jivesoftware.os.amza.service.AmzaPartitionCommitable; import com.jivesoftware.os.amza.service.AmzaPartitionUpdates; import com.jivesoftware.os.amza.service.storage.PartitionCreator; import com.jivesoftware.os.amza.service.storage.SystemWALStorage; import com.jivesoftware.os.aquarium.interfaces.StateStorage.StateUpdates; import com.jivesoftware.os.jive.utils.ordered.id.OrderIdProvider; import com.jivesoftware.os.mlogger.core.MetricLogger; import com.jivesoftware.os.mlogger.core.MetricLoggerFactory; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; /** * Created by jonathan.colt on 4/19/17. */ public class AmzaStateStorageFlusher { private static final MetricLogger LOG = MetricLoggerFactory.getLogger(); private final SystemWALStorage systemWALStorage; private final OrderIdProvider orderIdProvider; private final WALUpdated walUpdated; private final Semaphore semaphore = new Semaphore(Short.MAX_VALUE); private final AtomicReference<AmzaPartitionUpdates> partitionUpdates = new AtomicReference<>(new AmzaPartitionUpdates()); private final AtomicLong flushVersion = new AtomicLong(); public AmzaStateStorageFlusher(SystemWALStorage systemWALStorage, OrderIdProvider orderIdProvider, WALUpdated walUpdated) { this.systemWALStorage = systemWALStorage; this.orderIdProvider = orderIdProvider; this.walUpdated = walUpdated; Executors.newSingleThreadExecutor().submit(() -> { while (true) { try { long currentVersion = flushVersion.get(); flush(); synchronized (flushVersion) { if (currentVersion == flushVersion.get()) { flushVersion.wait(); } } } catch (Throwable t) { LOG.error("AmzaStateStorage failure", t); } } }); } public boolean update(PartitionName partitionName, byte context, StateUpdates<Long> updates) throws Exception { semaphore.acquire(); try { AmzaPartitionUpdates amzaPartitionUpdates = partitionUpdates.get(); updates.updates( (rootMember, otherMember, lifecycle, state, timestamp) -> { byte[] keyBytes = AmzaAquariumProvider.stateKey(partitionName, context, rootMember, lifecycle, otherMember); byte[] valueBytes = { state.getSerializedForm() }; amzaPartitionUpdates.set(keyBytes, valueBytes, timestamp); return true; }); flushVersion.incrementAndGet(); synchronized (flushVersion) { flushVersion.notifyAll(); } return false; } finally { semaphore.release(); } } public boolean flush() throws Exception { semaphore.acquire(Short.MAX_VALUE); try { AmzaPartitionUpdates amzaPartitionUpdates = partitionUpdates.get(); int size = amzaPartitionUpdates.size(); if (size > 0) { LOG.inc("stateStoreFlushed>pow>" + UIO.chunkPower(size, 0)); RowsChanged rowsChanged = systemWALStorage.update(PartitionCreator.AQUARIUM_STATE_INDEX, null, new AmzaPartitionCommitable(amzaPartitionUpdates, orderIdProvider), walUpdated); partitionUpdates.set(new AmzaPartitionUpdates()); return !rowsChanged.isEmpty(); } else { return false; } } finally { semaphore.release(Short.MAX_VALUE); } } }