com.gemstone.gemfire.internal.cache.HARegion Maven / Gradle / Ivy
Show all versions of gemfire-core Show documentation
/*
* Copyright (c) 2010-2015 Pivotal Software, 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. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.internal.cache;
import com.gemstone.gemfire.DataSerializable;
import com.gemstone.gemfire.cache.CacheLoader;
import com.gemstone.gemfire.cache.CacheLoaderException;
import com.gemstone.gemfire.cache.CacheWriterException;
import com.gemstone.gemfire.cache.EntryNotFoundException;
import com.gemstone.gemfire.cache.ExpirationAction;
import com.gemstone.gemfire.cache.ExpirationAttributes;
import com.gemstone.gemfire.cache.LoaderHelper;
import com.gemstone.gemfire.cache.Operation;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionAttributes;
import com.gemstone.gemfire.cache.RegionExistsException;
import com.gemstone.gemfire.cache.TimeoutException;
import com.gemstone.gemfire.distributed.internal.DistributionAdvisor.Profile;
import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
import com.gemstone.gemfire.internal.Assert;
import com.gemstone.gemfire.internal.cache.ha.HARegionQueue;
import com.gemstone.gemfire.internal.cache.ha.ThreadIdentifier;
import com.gemstone.gemfire.internal.cache.tier.sockets.ClientProxyMembershipID;
import com.gemstone.gemfire.internal.cache.tier.sockets.HAEventWrapper;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* This region is being implemented to suppress distribution of puts and to
* allow localDestroys on mirrored regions.
*
* @author Mitul Bid
* @since 4.3
*
*/
public final class HARegion extends DistributedRegion
{
CachePerfStats haRegionStats;
// Prevent this region from participating in a TX, bug 38709
@Override
public boolean isSecret() {
return true;
}
@Override
protected boolean deferRecovery() {
return true;
}
@Override
protected boolean isCopyOnRead() {
return false;
}
@Override
public boolean doesNotDistribute() {
return true;
}
@Override
protected StringBuilder getStringBuilder() {
StringBuilder buf = new StringBuilder();
buf.append("HARegion");
buf.append("[path='")
.append(getFullPath());
return buf;
}
// protected Object conditionalCopy(Object o) {
// return o;
// }
private volatile HARegionQueue owningQueue;
// private Map giiProviderStates;
/**
* @param regionName
* @param attrs
* @param parentRegion
* @param cache
*/
private HARegion(String regionName, RegionAttributes attrs,
LocalRegion parentRegion, GemFireCacheImpl cache) {
super(regionName, attrs, parentRegion, cache, new InternalRegionArguments()
.setDestroyLockFlag(true).setRecreateFlag(false)
.setSnapshotInputStream(null).setImageTarget(null));
this.haRegionStats = new DummyCachePerfStats();
}
@Override
public boolean allowsPersistence() {
return false;
}
/**
* Updates never distributed from buckets.
* @since 5.7
*/
@Override
protected void distributeUpdate(EntryEventImpl event, long lastModifiedTime) {
}
@Override
public void createEventTracker() {
// event trackers aren't needed for HARegions
}
/**
* void implementation over-riding the method to allow localDestroy on
* mirrored regions
*
* @param event
*/
@Override
protected void checkIfReplicatedAndLocalDestroy(EntryEventImpl event) {
}
@Override
void checkEntryTimeoutAction(String mode, ExpirationAction ea) {
}
/**
* Overriding this method so as to allow expiry action of local invalidate
* even if the scope is distributed mirrored.
*
* author Asif
*/
@Override
public ExpirationAttributes setEntryTimeToLive(ExpirationAttributes timeToLive)
{
//checkReadiness();
if (timeToLive == null) {
throw new IllegalArgumentException(LocalizedStrings.HARegion_TIMETOLIVE_MUST_NOT_BE_NULL.toLocalizedString());
}
if ((timeToLive.getAction() == ExpirationAction.LOCAL_DESTROY && this.dataPolicy
.withReplication())) {
throw new IllegalArgumentException(LocalizedStrings.HARegion_TIMETOLIVE_ACTION_IS_INCOMPATIBLE_WITH_THIS_REGIONS_MIRROR_TYPE.toLocalizedString());
}
if (!this.statisticsEnabled) {
throw new IllegalStateException(LocalizedStrings.HARegion_CANNOT_SET_TIME_TO_LIVE_WHEN_STATISTICS_ARE_DISABLED.toLocalizedString());
}
ExpirationAttributes oldAttrs = getEntryTimeToLive();
this.entryTimeToLive = timeToLive.getTimeout();
this.entryTimeToLiveExpirationAction = timeToLive.getAction();
setEntryTimeToLiveAtts();
updateEntryExpiryPossible();
timeToLiveChanged(oldAttrs);
return oldAttrs;
}
/**
* Before invalidating , check if the entry being invalidated has a key as
* Long . If yes check if the key is still present in availableIDs . If yes
* remove & allow invalidation to proceed. But if the key (Long)is absent do
* not allow invalidation to proceed.
*
*
author Asif
*/
@Override
protected void basicInvalidate(final EntryEventImpl event,
boolean invokeCallbacks, final boolean forceNewEntry)
throws EntryNotFoundException
{
Object key = event.getKey();
if (key instanceof Long) {
boolean removedFromAvID = false;
Conflatable conflatable = null;
try {
conflatable = (Conflatable)this.get(key);
removedFromAvID = !this.owningQueue.isPrimary()
&& this.owningQueue.destroyFromAvailableIDs((Long)key);
}
catch (InterruptedException ie) {
Thread.currentThread().interrupt();
getCancelCriterion().checkCancelInProgress(ie);
return;
}
if (!removedFromAvID) {
return;
}
//
if(conflatable instanceof HAEventWrapper) {
this.owningQueue
.decAndRemoveFromHAContainer((HAEventWrapper)conflatable);
}
//
//update the stats
//if(this.owningQueue.getLogger().fineEnabled()){
this.owningQueue.stats.incEventsExpired();
//}
}
this.entries.invalidate(event, invokeCallbacks, forceNewEntry,false);
return;
}
/**
* This method is over-ridden since we do not want GII of ThreadIdentifier
* objects to happen
*/
@Override
protected boolean checkEntryNotValid(RegionEntry mapEntry)
{
return (super.checkEntryNotValid(mapEntry) || mapEntry.getKey() instanceof ThreadIdentifier);
}
@Override
public final Object put(Object key, Object value, Object aCallbackArgument)
throws TimeoutException, CacheWriterException {
checkReadiness();
EntryEventImpl event = EntryEventImpl.create(this, Operation.UPDATE, key,
value, aCallbackArgument, false, getMyId());
try {
Object oldValue = null;
if (basicPut(event, false, // ifNew
false, // ifOld
null, // expectedOldValue
false // requireOldValue
)) {
oldValue = event.getOldValue();
}
return handleNotAvailable(oldValue);
} finally {
event.release();
}
}
/**
*
* Returns an instance of HARegion after it has properly initialized
*
* @param regionName
* name of the region to be created
* @param cache
* the cache that owns this region
* @param ra
* attributes of the region
* @return an instance of an HARegion
* @throws TimeoutException
* @throws RegionExistsException
* if a region of the same name exists in the same Cache
* @throws IOException
* @throws ClassNotFoundException
*/
public static HARegion getInstance(String regionName, GemFireCacheImpl cache,
HARegionQueue hrq, RegionAttributes ra) throws TimeoutException,
RegionExistsException, IOException, ClassNotFoundException
{
HARegion haRegion = new HARegion(regionName, ra, null, cache);
haRegion.setOwner(hrq);
Region region = cache.createVMRegion(regionName, ra,
new InternalRegionArguments().setInternalMetaRegion(haRegion)
.setDestroyLockFlag(true).setSnapshotInputStream(null)
.setImageTarget(null));
return (HARegion)region;
}
public boolean isPrimaryQueue() {
if (this.owningQueue != null) {
return this.owningQueue.isPrimary();
}
return false;
}
public HARegionQueue getOwner()
{
// fix for bug #41634 - don't release a reference to the owning queue until
// it is fully initialized. The previous implementation of this rule did
// not protect subclasses of HARegionQueue and caused the bug.
return this.owningQueue.isQueueInitialized()? this.owningQueue : null;
}
@Override
public CachePerfStats getCachePerfStats() {
return this.haRegionStats;
}
/**
* This method is used to set the HARegionQueue owning the HARegion. It is set
* after the HARegionQueue is properly constructed
*
* @param hrq
* The owning HARegionQueue instance
*/
public void setOwner(HARegionQueue hrq)
{
this.owningQueue = hrq;
}
@Override
final protected boolean shouldNotifyBridgeClients()
{
return false;
}
// re-implemented from LocalRegion to avoid recording the event in GemFireCache
// before it's applied to the cache's region
// public boolean hasSeenClientEvent(InternalCacheEvent event) {
// return false;
// }
protected void notifyGatewayHub(EnumListenerEvent operation,
EntryEventImpl event)
{
}
/**
* This method is overriden so as to make isOriginRemote true always so that
* the operation is never propagated to other nodes
*
* @see com.gemstone.gemfire.internal.cache.AbstractRegion#destroyRegion()
*/
@Override
public void destroyRegion(Object aCallbackArgument)
throws CacheWriterException, TimeoutException
{
// getLogWriterI18n().info(LocalizedStrings.DEBUG, "destroying HARegion " + this.getName(), new Exception("stack trace"));
//Do not generate EventID
RegionEventImpl event = new RegionEventImpl(this, Operation.REGION_DESTROY,
aCallbackArgument, true /* isOriginRemote */, getMyId());
basicDestroyRegion(event, true);
}
/**
* Never genearte EventID for any Entry or Region operation on the HARegion
*/
@Override
final public boolean generateEventID()
{
return false;
}
@Override
protected void initialize(InputStream snapshotInputStream,
InternalDistributedMember imageTarget,
InternalRegionArguments internalRegionArgs)
throws TimeoutException, IOException, ClassNotFoundException
{
// Set this region in the ProxyBucketRegion early so that profile exchange will
// perform the correct fillInProfile method
// try {
super.initialize(snapshotInputStream, imageTarget, internalRegionArgs);
// } finally {
// this.giiProviderStates = null;
// }
}
/**
* @return the deserialized value
* @see DistributedRegion#findObjectInSystem(KeyInfo, boolean,
* TXStateInterface, boolean, Object, boolean, boolean,
* ClientProxyMembershipID, EntryEventImpl, boolean, boolean)
*/
@Override
protected Object findObjectInSystem(KeyInfo keyInfo, boolean isCreate,
TXStateInterface txState, boolean generateCallbacks, Object localValue,
boolean disableCopyOnRead, boolean preferCD,
ClientProxyMembershipID requestingClient, EntryEventImpl clientEvent,
boolean returnTombstones, boolean allowReadFromHDFS) throws CacheLoaderException, TimeoutException {
Object value = null;
final Object key = keyInfo.getKey();
final Object aCallbackArgument = keyInfo.getCallbackArg();
// copy into local var to prevent race condition
RegionEntry re = null;
Assert.assertTrue(!hasServerProxy());
CacheLoader loader = basicGetLoader();
if (loader != null) {
final LoaderHelper loaderHelper = loaderHelperFactory.createLoaderHelper(
key, aCallbackArgument, false /* netSearchAllowed */,
true /* netloadallowed */, null/* searcher */);
try {
value = loader.load(loaderHelper);
}
finally {
}
if (value != null) {
try {
validateKey(key);
Operation op;
if (isCreate) {
op = Operation.LOCAL_LOAD_CREATE;
}
else {
op = Operation.LOCAL_LOAD_UPDATE;
}
EntryEventImpl event = EntryEventImpl.create(
this, op, key, value,
aCallbackArgument, false, getMyId(), generateCallbacks);
try {
re = basicPutEntry(event, 0L);
} finally {
event.release();
}
}
catch (CacheWriterException cwe) {
// @todo smenon Log the exception
}
}
}
if (isCreate) {
recordMiss(txState, re, key);
}
return value;
}
/**
* invoked when we start providing a GII image
*/
public void startServingGIIRequest() {
// some of our dunit tests create HARegions in odd ways that cause owningQueue
// to be null during GII
if (this.owningQueue == null) {
if (getLogWriterI18n().fineEnabled()) {
getLogWriterI18n().fine("found that owningQueue was null during GII of " + this + " which could lead to event loss (see #41681)");
}
return;
}
this.owningQueue.startGiiQueueing();
}
/**
* invoked when we finish providing a GII image
*/
public void endServingGIIRequest() {
if (this.owningQueue != null) {
this.owningQueue.endGiiQueueing();
}
}
@Override
protected CacheDistributionAdvisor createDistributionAdvisor(InternalRegionArguments internalRegionArgs) {
return HARegionAdvisor.createHARegionAdvisor(this); // Warning: potential early escape of object before full construction
}
@Override
public void fillInProfile(Profile p) {
super.fillInProfile(p);
HARegionAdvisor.HAProfile h = (HARegionAdvisor.HAProfile)p;
// dunit tests create HARegions without encapsulating them in queues
if (this.owningQueue != null) {
h.isPrimary = this.owningQueue.isPrimary();
h.hasRegisteredInterest = this.owningQueue.getHasRegisteredInterest();
}
}
/* (non-Javadoc)
* @see com.gemstone.gemfire.internal.cache.LocalRegion#getEventState()
*/
@Override
public Map extends DataSerializable, ? extends DataSerializable> getEventState() {
if (this.owningQueue != null) {
return this.owningQueue.getEventMapForGII();
}
return null;
}
/*
* Record cache event state for a potential initial image provider. This is
* used to install event state when the sender is selected as initial image
* provider.
* @param sender
* @param eventState
*/
@Override
public void recordEventState(InternalDistributedMember sender, Map eventState) {
if (eventState != null && this.owningQueue != null) {
this.owningQueue.recordEventState(sender, eventState);
}
}
/** send a distribution advisor profile update to other members */
public void sendProfileUpdate() {
new UpdateAttributesProcessor(this).distribute(false);
}
/**
* whether the primary queue for the client has registered interest, or
* there is no primary present
*/
public boolean noPrimaryOrHasRegisteredInterest() {
return ((HARegionAdvisor)this.distAdvisor).noPrimaryOrHasRegisteredInterest();
}
/** HARegions have their own advisors so that interest registration state can be tracked */
public static class HARegionAdvisor extends CacheDistributionAdvisor {
/**
* @param region
*/
private HARegionAdvisor(CacheDistributionAdvisee region) {
super(region);
}
public static HARegionAdvisor createHARegionAdvisor(CacheDistributionAdvisee region) {
HARegionAdvisor advisor = new HARegionAdvisor(region);
advisor.initialize();
return advisor;
}
/* (non-Javadoc)
* @see com.gemstone.gemfire.internal.cache.CacheDistributionAdvisor#adviseInitialImage(com.gemstone.gemfire.internal.cache.CacheDistributionAdvisor.InitialImageAdvice)
*/
@Override
public InitialImageAdvice adviseInitialImage(
InitialImageAdvice previousAdvice) {
InitialImageAdvice r = super.adviseInitialImage(previousAdvice);
r.setOthers(this.getAdvisee().getDistributionManager().getOtherDistributionManagerIds());
return r;
}
@Override
protected Profile instantiateProfile(
InternalDistributedMember memberId, int version) {
return new HAProfile(memberId, version);
}
public boolean noPrimaryOrHasRegisteredInterest() {
Profile[] locProfiles = this.profiles; // grab current profiles
for (int i=0; i