com.gemstone.gemfire.internal.cache.EntryExpiryTask Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gemfire-core Show documentation
Show all versions of gemfire-core Show documentation
SnappyData store based off Pivotal GemFireXD
/*
* 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;
/**
* EntryExpiryTask represents a timeout event for a region entry.
*/
//import com.gemstone.gemfire.LogWriterI18n;
//import com.gemstone.gemfire.GemFireCacheException;
import java.util.concurrent.locks.Lock;
import com.gemstone.gemfire.cache.CacheException;
import com.gemstone.gemfire.cache.EntryDestroyedException;
import com.gemstone.gemfire.cache.EntryNotFoundException;
import com.gemstone.gemfire.cache.ExpirationAction;
import com.gemstone.gemfire.cache.ExpirationAttributes;
import com.gemstone.gemfire.cache.Operation;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.TimeoutException;
import com.gemstone.gemfire.internal.InternalStatisticsDisabledException;
public class EntryExpiryTask extends ExpiryTask {
/**
* The region entry we are working with
*/
private RegionEntry re; // not final so cancel can null it out see bug 37574
/*
* This was added to accommodate a session replication requirement where an
* empty client has a need to access the expired entry so that additional
* processing can be performed on it.
*
* This field is nether private nor final so that dunits can manipulate it as
* necessary.
*/
public static boolean expireSendsEntryAsCallback =
Boolean.getBoolean("gemfire.EXPIRE_SENDS_ENTRY_AS_CALLBACK");
protected EntryExpiryTask(LocalRegion region, RegionEntry re) {
super(region);
this.re = re;
}
@Override
protected ExpirationAttributes getTTLAttributes() {
return getLocalRegion().getAttributes().getEntryTimeToLive();
}
@Override
protected ExpirationAttributes getIdleAttributes() {
return getLocalRegion().getAttributes().getEntryIdleTimeout();
}
protected RegionEntry getRegionEntry() {
return this.re;
}
/**
* Returns the tasks region entry if it "checks" out. The check is to
* see if the region entry still exists.
* @throws EntryNotFoundException if the task no longer has a region entry or
* if the region entry it has is removed.
*/
protected RegionEntry getCheckedRegionEntry() throws EntryNotFoundException {
RegionEntry result = this.re;
if (re == null || re.isDestroyedOrRemoved()) {
throw new EntryNotFoundException("expiration task no longer has access to region entry");
}
return result;
}
@Override
protected long getLastAccessedTime() throws EntryNotFoundException
{
RegionEntry re = getCheckedRegionEntry();
try {
return re.getLastAccessed();
}
catch (InternalStatisticsDisabledException e) {
return 0;
}
}
@Override
protected long getLastModifiedTime() throws EntryNotFoundException
{
return getCheckedRegionEntry().getLastModified();
}
private Object getValueForCallback(LocalRegion r, Object k) {
Region.Entry,?> e = r.getEntry(k);
return (e != null) ? e.getValue() : null;
}
private Object createExpireEntryCallback(LocalRegion r, Object k) {
return expireSendsEntryAsCallback ? getValueForCallback(r, k) : null;
}
@Override
protected boolean destroy(boolean isPending) throws CacheException
{
RegionEntry re = getCheckedRegionEntry();
Object key = re.getKeyCopy();
LocalRegion lr = getLocalRegion();
EntryEventImpl event = EntryEventImpl.create(
lr, Operation.EXPIRE_DESTROY, key, null,
createExpireEntryCallback(lr, key), false, lr.getMyId());
event.setPendingSecondaryExpireDestroy(isPending);
try {
if (lr.generateEventID()) {
event.setNewEventId(lr.getCache().getDistributedSystem());
}
lr.expireDestroy(event, true); // expectedOldValue
return true;
} finally {
event.release();
}
}
@Override
protected boolean invalidate() throws TimeoutException,
EntryNotFoundException
{
RegionEntry re = getCheckedRegionEntry();
Object key = re.getKeyCopy();
LocalRegion lr = getLocalRegion();
EntryEventImpl event = EntryEventImpl.create(lr,
Operation.EXPIRE_INVALIDATE, key, null,
createExpireEntryCallback(lr, key), false, lr.getMyId());
try {
if (lr.generateEventID()) {
event.setNewEventId(lr.getCache().getDistributedSystem());
}
lr.expireInvalidate(event);
} finally {
event.release();
}
return true;
}
@Override
protected boolean localDestroy() throws CacheException
{
RegionEntry re = getCheckedRegionEntry();
Object key = re.getKeyCopy();
LocalRegion lr = getLocalRegion();
EntryEventImpl event = EntryEventImpl.create(lr,
Operation.EXPIRE_LOCAL_DESTROY, key, null,
createExpireEntryCallback(lr, key), false, lr.getMyId());
try {
if (lr.generateEventID()) {
event.setNewEventId(lr.getCache().getDistributedSystem());
}
lr.expireDestroy(event, false); // expectedOldValue
} finally {
event.release();
}
return true;
}
@Override
protected boolean localInvalidate() throws EntryNotFoundException
{
RegionEntry re = getCheckedRegionEntry();
Object key = re.getKeyCopy();
LocalRegion lr = getLocalRegion();
EntryEventImpl event = EntryEventImpl.create(lr,
Operation.EXPIRE_LOCAL_INVALIDATE, key, null,
createExpireEntryCallback(lr, key), false, lr.getMyId());
try {
if (lr.generateEventID()) {
event.setNewEventId(lr.getCache().getDistributedSystem());
}
lr.expireInvalidate(event);
} finally {
event.release();
}
return true;
}
@Override
final protected void reschedule() throws CacheException
{
if (isCacheClosing() || getLocalRegion().isClosed() || getLocalRegion().isDestroyed()
|| !isExpirationAllowed()) {
return;
}
if (getExpirationTime() > 0) {
addExpiryTask();
}
}
@Override
protected void addExpiryTask() throws EntryNotFoundException
{
getLocalRegion().addExpiryTask(getCheckedRegionEntry());
}
@Override
public String toString()
{
String result = super.toString();
RegionEntry re = this.re;
if (re != null) {
result += ", " + re.getKey();
}
return result;
}
@Override
protected void performTimeout() throws CacheException
{
// remove the task from the region's map first thing
// so the next call to addExpiryTaskIfAbsent will
// add a new task instead of doing nothing, which would
// erroneously cancel expiration for this key.
getLocalRegion().cancelExpiryTask(this.re);
getLocalRegion().performExpiryTimeout(this);
}
@Override
public boolean isPending() {
RegionEntry re = this.re;
if(re == null) {
return false;
}
if (re.isDestroyedOrRemoved()) {
return false;
}
ExpirationAction action = getAction();
if (action == null) {
return false;
}
if((action.isInvalidate() || action.isLocalInvalidate()) && re.isInvalid()) {
return false;
}
return true;
}
@Override
protected ExpirationAction getAction() {
long ttl = getTTLAttributes().getTimeout();
long idle = getIdleAttributes().getTimeout();
ExpirationAction action;
if (ttl == 0) {
action = getIdleAttributes().getAction();
}
else
if (idle != 0 && idle < ttl) {
action = getIdleAttributes().getAction();
}
else {
action = getTTLAttributes().getAction();
}
return action;
}
/**
* Called by LocalRegion#performExpiryTimeout
*/
@Override
protected void basicPerformTimeout(boolean isPending) throws CacheException
{
if (!isExpirationAllowed()) {
return;
}
if (!isExpirationPossible()) {
reschedule();
return;
}
// Need to figure out why it expired - ttl, or idle timeout?
ExpirationAction action;
long ttl = getTTLAttributes().getTimeout();
long idle = getIdleAttributes().getTimeout();
if (ttl == 0) {
action = getIdleAttributes().getAction();
}
else
if (idle != 0 && idle < ttl) {
action = getIdleAttributes().getAction();
}
else {
action = getTTLAttributes().getAction();
}
// if global scope get distributed lock for destroy and invalidate actions
if (getLocalRegion().getScope().isGlobal()
&& (action.isDestroy() || action.isInvalidate())) {
Lock lock = getLocalRegion().getDistributedLock(
getCheckedRegionEntry().getKeyCopy());
lock.lock();
try {
long expTime = getExpirationTime();
if (expTime == 0L) {
return;
}
if (getNow() >= expTime) {
if (getLoggerI18n().finerEnabled()) {
String msg = this.toString() + ".expire(" + action
+ "). ttlExpiration: " + ttl + ", idleExpiration: "
+ idle + ", ttlAttrs: " + getTTLAttributes()
+ ", idleAttrs: " + getIdleAttributes() + " action is: "
+ action;
getLoggerI18n().finer(
this.toString() + ".performTimeout(). getExpirationTime() is "
+ expTime + "; " + msg);
}
expire(action, isPending);
return;
}
}
finally {
lock.unlock();
}
}
else {
if (getLoggerI18n().finerEnabled()) {
getLoggerI18n().finer(
this.toString() + ".performTimeout(). getExpirationTime() is "
+ getExpirationTime());
}
expire(isPending);
return;
}
reschedule();
}
@Override
public Object getKey() {
RegionEntry entry = this.re;
if (entry == null) {
throw new EntryDestroyedException();
}
// getKeyCopy() not required here since this is invoked by
// BucketRegion.performExpiryTimeout for local accounting in
// pendingSecondaryExpires and map lookups against RegionEntry as key will
// be fine in GemXD
return entry.getKey();
}
@Override
public boolean cancel() {
boolean superCancel = super.cancel();
if (superCancel) {
this.re = null;
}
return superCancel;
}
}