blob: 12ed1144ec538d31672323873c13053b8d7d1507 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.distributed.internal.locks;
import java.util.HashMap;
import org.apache.logging.log4j.Logger;
import org.apache.geode.InternalGemFireError;
import org.apache.geode.distributed.internal.DistributionManager;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.apache.geode.internal.Assert;
import org.apache.geode.internal.logging.log4j.LogMarker;
import org.apache.geode.logging.internal.log4j.api.LogService;
/**
* Keeps track of all the information kept by the elder.
*
* @since GemFire 4.0
*/
public class ElderState {
private static final Logger logger = LogService.getLogger();
/**
* Maps service name keys to GrantorInfo values.
*/
private final HashMap<String, GrantorInfo> nameToInfo;
private final DistributionManager dm;
/**
* Constructs the EdlerState for the given dm. Note that this constructor does not complete until
* elder recovery is complete.
*/
public ElderState(DistributionManager dm) {
Assert.assertTrue(dm != null);
this.dm = dm;
this.nameToInfo = new HashMap<>();
try {
this.dm.getStats().incElders(1);
ElderInitProcessor.init(this.dm, this.nameToInfo);
} catch (NullPointerException | InternalGemFireError e) {
try {
checkForProblem(dm);
} finally {
throw e;
}
} finally {
if (logger.isTraceEnabled(LogMarker.DLS_VERBOSE)) {
StringBuilder sb = new StringBuilder("ElderState initialized with:");
for (String key : this.nameToInfo.keySet()) {
sb.append("\n\t").append(key).append(": ").append(this.nameToInfo.get(key));
}
logger.trace(LogMarker.DLS_VERBOSE, sb.toString());
}
}
}
private void checkForProblem(DistributionManager checkDM) {
if (checkDM.getSystem() == null) {
logger.warn(LogMarker.DLS_MARKER, "ElderState problem: system={}", checkDM.getSystem());
return;
}
if (checkDM.getSystem().getDistributionManager() == null) {
logger.warn(LogMarker.DLS_MARKER,
"ElderState problem: system DistributionManager={}",
checkDM.getSystem().getDistributionManager());
}
if (checkDM != checkDM.getSystem().getDistributionManager()) {
logger.warn(LogMarker.DLS_MARKER,
"ElderState problem: dm={}, but system DistributionManager={}",
new Object[] {checkDM, checkDM.getSystem().getDistributionManager()});
}
}
/**
* Atomically determine who is the current grantor of the given service. If no current grantor
* exists then the caller is made the grantor.
*
* @param serviceName the name of the lock service we want the grantor of
* @param requestor the id of the member who is making this request
* @return the current grantor of <code>serviceName</code> and recoveryNeeded will be true if
* requestor has become the grantor and needs to recover lock info.
*/
public GrantorInfo getGrantor(String serviceName, InternalDistributedMember requestor,
int dlsSerialNumberRequestor) {
synchronized (this) {
GrantorInfo gi = (GrantorInfo) this.nameToInfo.get(serviceName);
if (gi != null) {
waitWhileInitiatingTransfer(gi);
InternalDistributedMember currentGrantor = gi.getId();
// Note that elder recovery may put GrantorInfo instances in
// the map whose id is null and whose needRecovery is true
if (currentGrantor != null
&& this.dm.getDistributionManagerIds().contains(currentGrantor)) {
return gi;
} else {
if (logger.isTraceEnabled(LogMarker.DLS_VERBOSE)) {
logger.trace(LogMarker.DLS_VERBOSE, "Elder setting grantor for {} to {} because {} ",
serviceName, requestor, (currentGrantor != null ? "current grantor crashed"
: "of unclean grantor shutdown"));
}
// current grantor crashed; make new member grantor and force recovery
long myVersion = gi.getVersionId() + 1;
this.nameToInfo.put(serviceName,
new GrantorInfo(requestor, myVersion, dlsSerialNumberRequestor, false));
return new GrantorInfo(requestor, myVersion, dlsSerialNumberRequestor, true);
}
} else {
if (logger.isTraceEnabled(LogMarker.DLS_VERBOSE)) {
logger.trace(LogMarker.DLS_VERBOSE,
"Elder setting grantor for {} to {} because of clean grantor shutdown", serviceName,
requestor);
}
gi = new GrantorInfo(requestor, 1, dlsSerialNumberRequestor, false);
this.nameToInfo.put(serviceName, gi);
return gi;
}
}
}
/**
* Atomically determine who is the current grantor of the given service.
*
* @param serviceName the name of the lock service we want the grantor of
* @return the current grantor of <code>serviceName</code> and recoveryNeeded will be true if
* requestor has become the grantor and needs to recover lock info.
*/
public GrantorInfo peekGrantor(String serviceName) {
synchronized (this) {
GrantorInfo gi = (GrantorInfo) this.nameToInfo.get(serviceName);
if (gi != null) {
waitWhileInitiatingTransfer(gi);
InternalDistributedMember currentGrantor = gi.getId();
// Note that elder recovery may put GrantorInfo instances in
// the map whose id is null and whose needRecovery is true
if (currentGrantor != null
&& this.dm.getDistributionManagerIds().contains(currentGrantor)) {
return gi;
} else {
return new GrantorInfo(null, 0, 0, true);
}
} else {
return new GrantorInfo(null, 0, 0, false);
}
}
}
/**
* Atomically sets the current grantor of the given service to <code>newGrantor</code>.
*
* @param serviceName the name of the lock service we want the grantor of
* @param newGrantor the id of the member who is making this request
* @param oldTurk if non-null then only do the become if the current grantor is the oldTurk
* @return the previous grantor, which may be null, of <code>serviceName</code> and recoveryNeeded
* will be true if new grantor needs to recover lock info
*/
public GrantorInfo becomeGrantor(String serviceName, InternalDistributedMember newGrantor,
int newGrantorSerialNumber, InternalDistributedMember oldTurk) {
GrantorInfo newInfo = null;
InternalDistributedMember previousGrantor = null;
long newGrantorVersion = -1;
try {
synchronized (this) {
GrantorInfo gi = (GrantorInfo) this.nameToInfo.get(serviceName);
while (gi != null && gi.isInitiatingTransfer()) {
waitWhileInitiatingTransfer(gi);
gi = (GrantorInfo) this.nameToInfo.get(serviceName);
}
if (gi != null) {
previousGrantor = gi.getId();
// Note that elder recovery may put GrantorInfo instances in
// the map whose id is null and whose needRecovery is true
// if previousGrantor still exists...
if (previousGrantor != null
&& this.dm.getDistributionManagerIds().contains(previousGrantor)) {
// if newGrantor is not previousGrantor...
if (!newGrantor.equals(previousGrantor)) {
// problem: specified oldTurk is not previousGrantor...
if (oldTurk != null && !oldTurk.equals(previousGrantor)) {
if (logger.isTraceEnabled(LogMarker.DLS_VERBOSE)) {
logger.trace(LogMarker.DLS_VERBOSE,
"Elder did not become grantor for {} to {} because oldT was {} and the current grantor is {}",
serviceName, newGrantor, oldTurk, previousGrantor);
}
}
// no oldTurk or oldTurk matches previousGrantor... transfer might occur
else {
// install new grantor
if (logger.isTraceEnabled(LogMarker.DLS_VERBOSE)) {
logger.trace(LogMarker.DLS_VERBOSE, "Elder forced to set grantor for {} to {}",
serviceName, newGrantor);
}
long myVersion = gi.getVersionId() + 1;
newGrantorVersion = myVersion;
newInfo = new GrantorInfo(newGrantor, myVersion, newGrantorSerialNumber, false);
this.nameToInfo.put(serviceName, newInfo);
if (gi.getId() != null && (oldTurk == null || gi.getId().equals(oldTurk))
&& !gi.getId().equals(newGrantor)) {
beginInitiatingTransfer(newInfo);
}
}
}
// return previous grantor
return new GrantorInfo(gi.getId(), gi.getVersionId(), gi.getSerialNumber(), true);
}
// no previousGrantor in existence...
else {
long myVersion = gi.getVersionId() + 1;
// problem: oldTurk was specified but there is no previousGrantor...
if (oldTurk != null) {
if (logger.isTraceEnabled(LogMarker.DLS_VERBOSE)) {
logger.trace(LogMarker.DLS_VERBOSE,
"Elder did not become grantor for {} to {} because oldT was {} and the current grantor {} had crashed",
serviceName, newGrantor, oldTurk, previousGrantor);
}
}
// no oldTurk was specified...
else {
if (logger.isTraceEnabled(LogMarker.DLS_VERBOSE)) {
logger.trace(LogMarker.DLS_VERBOSE,
"Elder forced to set grantor for {} to {} and noticed previous grantor had crashed",
serviceName, newGrantor);
}
// current grantor crashed; make new member grantor and force recovery
this.nameToInfo.put(serviceName,
new GrantorInfo(newGrantor, myVersion, newGrantorSerialNumber, false));
}
return new GrantorInfo(null, myVersion - 1, gi.getSerialNumber(), true);
}
}
// GrantorInfo was null...
else {
// problem: no oldTurk was specified
if (oldTurk != null) {
if (logger.isTraceEnabled(LogMarker.DLS_VERBOSE)) {
logger.trace(LogMarker.DLS_VERBOSE,
"Elder did not become grantor for {} to {} because oldT was {} and elder had no current grantor",
serviceName, newGrantor, oldTurk);
}
}
// no oldTurk was specified
else {
if (logger.isTraceEnabled(LogMarker.DLS_VERBOSE)) {
logger.trace(LogMarker.DLS_VERBOSE,
"Elder forced to set grantor for {} to {} because of clean grantor shutdown",
serviceName, newGrantor);
}
// no current grantor; last one shutdown cleanly
gi = new GrantorInfo(newGrantor, 1, newGrantorSerialNumber, false);
this.nameToInfo.put(serviceName, gi);
}
return new GrantorInfo(null, 0, 0, false);
}
}
} finally {
if (isInitiatingTransfer(newInfo)) {
Assert.assertTrue(newGrantorVersion > -1);
DeposeGrantorProcessor.send(serviceName, previousGrantor, newGrantor, newGrantorVersion,
newGrantorSerialNumber, dm);
finishInitiatingTransfer(newInfo);
}
}
}
/**
* Atomically clears the current grantor of the given service if the current grantor is
* <code>oldGrantor</code>. The next grantor for this service will not need to recover unless
* <code>locksHeld</code> is true.
*
* @param locksHeld true if old grantor had held locks
*/
public void clearGrantor(long grantorVersion, String serviceName, int dlsSerialNumber,
InternalDistributedMember oldGrantor, boolean locksHeld) {
synchronized (this) {
if (grantorVersion == -1) {
// not possible to clear grantor of non-initialized grantorVersion
return;
}
GrantorInfo currentGI = (GrantorInfo) this.nameToInfo.get(serviceName);
if (currentGI == null) {
// added null check because becomeGrantor may not have talked to elder before destroy dls
return;
}
if (currentGI.getVersionId() != grantorVersion
|| currentGI.getSerialNumber() != dlsSerialNumber) {
// not possible to clear mismatched grantorVersion
return;
}
GrantorInfo gi;
if (locksHeld) {
gi = (GrantorInfo) this.nameToInfo.put(serviceName,
new GrantorInfo(null, currentGI.getVersionId(), 0, true));
} else {
gi = (GrantorInfo) this.nameToInfo.remove(serviceName);
}
if (gi != null) {
InternalDistributedMember currentGrantor = gi.getId();
if (!oldGrantor.equals(currentGrantor)) { // fix for 32603
this.nameToInfo.put(serviceName, gi);
if (logger.isTraceEnabled(LogMarker.DLS_VERBOSE)) {
logger.trace(LogMarker.DLS_VERBOSE,
"Elder not making {} grantor shutdown for {} by {} because the current grantor is {}",
(locksHeld ? "unclean" : "clean"), serviceName, oldGrantor, currentGrantor);
}
} else {
if (logger.isTraceEnabled(LogMarker.DLS_VERBOSE)) {
logger.trace(LogMarker.DLS_VERBOSE, "Elder making {} grantor shutdown for {} by {}",
(locksHeld ? "unclean" : "clean"), serviceName, oldGrantor);
}
}
}
}
}
private boolean isInitiatingTransfer(GrantorInfo gi) {
if (gi == null)
return false;
synchronized (this) {
return gi.isInitiatingTransfer();
}
}
private void beginInitiatingTransfer(GrantorInfo gi) {
synchronized (this) {
gi.setInitiatingTransfer(true);
}
}
private void finishInitiatingTransfer(GrantorInfo gi) {
synchronized (this) {
gi.setInitiatingTransfer(false);
notifyAll();
}
}
private void waitWhileInitiatingTransfer(GrantorInfo gi) {
synchronized (this) {
boolean interrupted = false;
try {
while (gi.isInitiatingTransfer()) {
try {
wait();
} catch (InterruptedException e) {
interrupted = true;
dm.getCancelCriterion().checkCancelInProgress(e);
}
}
} finally {
if (interrupted)
Thread.currentThread().interrupt();
}
}
}
/** Testing method to force grantor recovery state for named service */
public void forceGrantorRecovery(String serviceName) {
synchronized (this) {
GrantorInfo gi = (GrantorInfo) this.nameToInfo.get(serviceName);
if (gi.isInitiatingTransfer()) {
throw new IllegalStateException(
"Cannot force grantor recovery for grantor that is transferring");
}
this.nameToInfo.put(serviceName,
new GrantorInfo(gi.getId(), gi.getVersionId(), gi.getSerialNumber(), true));
}
}
}