Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.hazelcast.replicatedmap.impl.record.AbstractReplicatedRecordStore Maven / Gradle / Ivy
/*
* 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.replicatedmap.impl.record;
import com.hazelcast.cluster.Address;
import com.hazelcast.cluster.Member;
import com.hazelcast.cluster.memberselector.MemberSelectors;
import com.hazelcast.internal.serialization.Data;
import com.hazelcast.internal.util.Clock;
import com.hazelcast.internal.util.Timer;
import com.hazelcast.replicatedmap.impl.ReplicatedMapEventPublishingService;
import com.hazelcast.replicatedmap.impl.ReplicatedMapService;
import com.hazelcast.replicatedmap.impl.operation.ReplicateUpdateOperation;
import com.hazelcast.replicatedmap.impl.operation.VersionResponsePair;
import com.hazelcast.spi.impl.operationservice.OperationService;
import com.hazelcast.spi.merge.SplitBrainMergePolicy;
import com.hazelcast.spi.merge.SplitBrainMergeTypes.ReplicatedMapMergeTypes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static com.hazelcast.core.EntryEventType.EVICTED;
import static com.hazelcast.internal.util.Preconditions.isNotNull;
import static com.hazelcast.replicatedmap.impl.ReplicatedMapService.SERVICE_NAME;
import static com.hazelcast.spi.impl.merge.MergingValueFactory.createMergingEntry;
/**
* This is the base class for all {@link ReplicatedRecordStore} implementations
*
* @param key type
* @param value type
*/
public abstract class AbstractReplicatedRecordStore extends AbstractBaseReplicatedRecordStore {
public AbstractReplicatedRecordStore(String name, ReplicatedMapService replicatedMapService, int partitionId) {
super(name, replicatedMapService, partitionId);
}
@Override
public Object remove(Object key) {
InternalReplicatedMapStorage storage = getStorage();
Object old = remove(storage, key);
storage.incrementVersion();
return old;
}
@Override
public Object removeWithVersion(Object key, long version) {
InternalReplicatedMapStorage storage = getStorage();
Object old = remove(storage, key);
storage.setVersion(version);
return old;
}
@SuppressWarnings("unchecked")
private Object remove(InternalReplicatedMapStorage storage, Object key) {
isNotNull(key, "key");
long startNanos = Timer.nanos();
V oldValue;
K marshalledKey = (K) marshall(key);
ReplicatedRecord current = storage.get(marshalledKey);
if (current == null) {
oldValue = null;
} else {
oldValue = current.getValueInternal();
storage.remove(marshalledKey, current);
}
if (replicatedMapConfig.isStatisticsEnabled()) {
getStats().incrementRemovesNanos(Timer.nanosElapsed(startNanos));
}
cancelTtlEntry(marshalledKey);
return oldValue;
}
@Override
@SuppressWarnings("unchecked")
public void evict(Object key) {
isNotNull(key, "key");
long startNanos = Timer.nanos();
V oldValue;
K marshalledKey = (K) marshall(key);
InternalReplicatedMapStorage storage = getStorage();
ReplicatedRecord current = storage.get(marshalledKey);
if (current == null) {
oldValue = null;
} else {
oldValue = current.getValueInternal();
storage.remove(marshalledKey, current);
}
Data dataKey = nodeEngine.toData(key);
Data dataOldValue = nodeEngine.toData(oldValue);
ReplicatedMapEventPublishingService eventPublishingService = replicatedMapService.getEventPublishingService();
eventPublishingService.fireEntryListenerEvent(dataKey, dataOldValue, null, EVICTED, name, nodeEngine.getThisAddress());
if (replicatedMapConfig.isStatisticsEnabled()) {
getStats().incrementRemovesNanos(Timer.nanosElapsed(startNanos));
}
}
@Override
public Object get(Object key) {
isNotNull(key, "key");
long startNanos = Timer.nanos();
ReplicatedRecord replicatedRecord = getStorage().get(marshall(key));
// Force return null on ttl expiration (but before cleanup thread run)
long ttlMillis = replicatedRecord == null ? 0 : replicatedRecord.getTtlMillis();
if (ttlMillis > 0 && Clock.currentTimeMillis() - replicatedRecord.getUpdateTime() >= ttlMillis) {
replicatedRecord = null;
}
Object value = replicatedRecord == null ? null : unmarshall(replicatedRecord.getValue());
if (replicatedMapConfig.isStatisticsEnabled()) {
getStats().incrementGetsNanos(Timer.nanosElapsed(startNanos));
}
return value;
}
@Override
public Object put(Object key, Object value) {
isNotNull(key, "key");
isNotNull(value, "value");
return put(key, value, 0, TimeUnit.MILLISECONDS, true);
}
@Override
public Object put(Object key, Object value, long ttl, TimeUnit timeUnit, boolean incrementHits) {
InternalReplicatedMapStorage storage = getStorage();
Object old = put(storage, key, value, ttl, timeUnit, incrementHits);
storage.incrementVersion();
return old;
}
@Override
public Object putWithVersion(Object key, Object value, long ttl, TimeUnit timeUnit, boolean incrementHits, long version) {
InternalReplicatedMapStorage storage = getStorage();
Object old = put(storage, key, value, ttl, timeUnit, incrementHits);
storage.setVersion(version);
return old;
}
@SuppressWarnings("unchecked")
private Object put(InternalReplicatedMapStorage storage, Object key, Object value,
long ttl, TimeUnit timeUnit, boolean incrementHits) {
isNotNull(key, "key");
isNotNull(value, "value");
isNotNull(timeUnit, "timeUnit");
if (ttl < 0) {
throw new IllegalArgumentException("ttl must be a positive integer");
}
long startNanos = Timer.nanos();
V oldValue = null;
K marshalledKey = (K) marshall(key);
V marshalledValue = (V) marshall(value);
long ttlMillis = ttl == 0 ? 0 : timeUnit.toMillis(ttl);
ReplicatedRecord old = storage.get(marshalledKey);
ReplicatedRecord record;
if (old == null) {
record = buildReplicatedRecord(marshalledKey, marshalledValue, ttlMillis);
storage.put(marshalledKey, record);
} else {
oldValue = old.getValueInternal();
if (incrementHits) {
old.setValue(marshalledValue, ttlMillis);
} else {
old.setValueInternal(marshalledValue, ttlMillis);
}
storage.put(marshalledKey, old);
}
if (ttlMillis > 0) {
scheduleTtlEntry(ttlMillis, marshalledKey, marshalledValue);
} else {
cancelTtlEntry(marshalledKey);
}
if (replicatedMapConfig.isStatisticsEnabled()) {
getStats().incrementPutsNanos(Timer.nanosElapsed(startNanos));
}
return oldValue;
}
@Override
public boolean containsKey(Object key) {
isNotNull(key, "key");
getStats().incrementOtherOperations();
return containsKeyAndValue(key);
}
// IMPORTANT >> Increments hit counter
private boolean containsKeyAndValue(Object key) {
ReplicatedRecord replicatedRecord = getStorage().get(marshall(key));
return replicatedRecord != null && replicatedRecord.getValue() != null;
}
@Override
public boolean containsValue(Object value) {
isNotNull(value, "value");
getStats().incrementOtherOperations();
Object v = unmarshall(value);
for (Map.Entry> entry : getStorage().entrySet()) {
V entryValue = entry.getValue().getValue();
if (v == entryValue || (entryValue != null && unmarshall(entryValue).equals(v))) {
return true;
}
}
return false;
}
@Override
public Set keySet(boolean lazy) {
getStats().incrementOtherOperations();
if (lazy) {
// Lazy evaluation to prevent to much copying
return new LazySet<>(new KeySetIteratorFactory<>(this), getStorage());
}
return getStorage().keySet();
}
@Override
public Collection values(boolean lazy) {
getStats().incrementOtherOperations();
if (lazy) {
// Lazy evaluation to prevent to much copying
return new LazyCollection<>(new ValuesIteratorFactory<>(this), getStorage());
}
return getStorage().values();
}
@Override
public Collection values(Comparator comparator) {
InternalReplicatedMapStorage storage = getStorage();
List values = new ArrayList<>(storage.size());
for (ReplicatedRecord record : storage.values()) {
values.add(unmarshall(record.getValue()));
}
getStats().incrementOtherOperations();
return values;
}
@Override
public Set entrySet(boolean lazy) {
getStats().incrementOtherOperations();
if (lazy) {
// Lazy evaluation to prevent to much copying
return new LazySet<>(new EntrySetIteratorFactory<>(this), getStorage());
}
return getStorage().entrySet();
}
@Override
public ReplicatedRecord getReplicatedRecord(Object key) {
isNotNull(key, "key");
return getStorage().get(marshall(key));
}
@Override
public boolean isEmpty() {
getStats().incrementOtherOperations();
return getStorage().isEmpty();
}
@Override
public int size() {
getStats().incrementOtherOperations();
return getStorage().size();
}
@Override
public void clear() {
clearInternal().incrementVersion();
}
@Override
public void clearWithVersion(long version) {
clearInternal().setVersion(version);
}
@Override
public void reset() {
destroy();
}
@Override
public Iterator recordIterator() {
return new RecordIterator(getStorage().entrySet().iterator());
}
public void putRecords(Collection records, long version) {
InternalReplicatedMapStorage storage = getStorage();
for (RecordMigrationInfo record : records) {
putRecord(storage, record);
}
storage.syncVersion(version);
}
@SuppressWarnings("unchecked")
private void putRecord(InternalReplicatedMapStorage storage, RecordMigrationInfo record) {
K key = (K) marshall(record.getKey());
V value = (V) marshall(record.getValue());
ReplicatedRecord newRecord = buildReplicatedRecord(key, value, record.getTtl());
newRecord.setHits(record.getHits());
newRecord.setCreationTime(record.getCreationTime());
newRecord.setLastAccessTime(record.getLastAccessTime());
newRecord.setUpdateTime(record.getLastUpdateTime());
storage.put(key, newRecord);
if (record.getTtl() > 0) {
scheduleTtlEntry(record.getTtl(), key, value);
}
}
private ReplicatedRecord buildReplicatedRecord(K key, V value, long ttlMillis) {
return new ReplicatedRecord<>(key, value, ttlMillis);
}
@Override
@SuppressWarnings("unchecked")
public boolean merge(ReplicatedMapMergeTypes mergingEntry,
SplitBrainMergePolicy, Object> mergePolicy) {
mergingEntry = (ReplicatedMapMergeTypes)
serializationService.getManagedContext().initialize(mergingEntry);
mergePolicy = (SplitBrainMergePolicy, Object>)
serializationService.getManagedContext().initialize(mergePolicy);
K marshalledKey = (K) marshall(mergingEntry.getRawKey());
InternalReplicatedMapStorage storage = getStorage();
ReplicatedRecord record = storage.get(marshalledKey);
if (record == null) {
V newValue = (V) mergePolicy.merge(mergingEntry, null);
if (newValue == null) {
return false;
}
record = buildReplicatedRecord(marshalledKey, newValue, 0);
storage.put(marshalledKey, record);
storage.incrementVersion();
Data dataKey = serializationService.toData(marshalledKey);
Data dataValue = serializationService.toData(newValue);
VersionResponsePair responsePair = new VersionResponsePair(mergingEntry.getRawValue(), getVersion());
sendReplicationOperation(false, name, dataKey, dataValue, record.getTtlMillis(), responsePair);
} else {
ReplicatedMapMergeTypes existingEntry = createMergingEntry(serializationService, record);
V newValue = (V) mergePolicy.merge(mergingEntry, existingEntry);
if (newValue == null) {
storage.remove(marshalledKey, record);
storage.incrementVersion();
Data dataKey = serializationService.toData(marshalledKey);
VersionResponsePair responsePair = new VersionResponsePair(mergingEntry.getRawValue(), getVersion());
sendReplicationOperation(true, name, dataKey, null, record.getTtlMillis(), responsePair);
return false;
}
record.setValueInternal(newValue, record.getTtlMillis());
storage.incrementVersion();
Data dataKey = serializationService.toData(marshalledKey);
Data dataValue = serializationService.toData(newValue);
VersionResponsePair responsePair = new VersionResponsePair(mergingEntry.getRawValue(), getVersion());
sendReplicationOperation(false, name, dataKey, dataValue, record.getTtlMillis(), responsePair);
}
return true;
}
private void sendReplicationOperation(boolean isRemove, String name, Data key, Data value, long ttl,
VersionResponsePair response) {
Collection members = nodeEngine.getClusterService().getMembers(MemberSelectors.DATA_MEMBER_SELECTOR);
for (Member member : members) {
invoke(isRemove, member.getAddress(), name, key, value, ttl, response);
}
}
private void invoke(boolean isRemove, Address address, String name, Data key, Data value, long ttl,
VersionResponsePair response) {
OperationService operationService = nodeEngine.getOperationService();
ReplicateUpdateOperation updateOperation = new ReplicateUpdateOperation(name, key, value, ttl,
response, isRemove, nodeEngine.getThisAddress());
updateOperation.setPartitionId(partitionId);
updateOperation.setValidateTarget(false);
operationService.invokeOnTarget(SERVICE_NAME, updateOperation, address);
}
private final class RecordIterator implements Iterator> {
private final Iterator>> iterator;
private Map.Entry> entry;
private RecordIterator(Iterator>> iterator) {
this.iterator = iterator;
}
@Override
public boolean hasNext() {
while (iterator.hasNext()) {
entry = iterator.next();
if (testEntry(entry)) {
return true;
}
}
return false;
}
@Override
public ReplicatedRecord next() {
Map.Entry> entry = this.entry;
ReplicatedRecord record = entry != null ? entry.getValue() : null;
while (entry == null) {
entry = findNextEntry();
Object key = entry.getKey();
record = entry.getValue();
Object value = record != null ? record.getValue() : null;
if (key != null && value != null) {
break;
}
}
this.entry = null;
return record;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Lazy structures are not modifiable");
}
private boolean testEntry(Map.Entry> entry) {
return entry.getKey() != null && entry.getValue() != null && !entry.getValue().isTombstone();
}
private Map.Entry> findNextEntry() {
Map.Entry> entry = null;
while (iterator.hasNext()) {
entry = iterator.next();
if (testEntry(entry)) {
break;
}
entry = null;
}
if (entry == null) {
throw new NoSuchElementException();
}
return entry;
}
}
}