/*
* Copyright 2008-2012 LinkedIn, Inc
*
* 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 voldemort.client.scheduler;
import java.util.Properties;
import java.util.concurrent.Callable;
import org.apache.log4j.Logger;
import voldemort.client.SystemStoreRepository;
import voldemort.store.InvalidMetadataException;
import voldemort.store.system.SystemStoreConstants;
import voldemort.utils.MetadataVersionStoreUtils;
/**
* The AsyncMetadataVersionManager is used to track the Metadata version on the
* cluster and if necessary Re-bootstrap the client.
*
* During initialization, it will retrieve the current version of the
* cluster.xml and then periodically check whether this has been updated. During
* init if the initial version turns out to be null, it means that no change has
* been done to that store since it was created. In this case, we assume version
* '0'.
*
* @author csoman
*
*/
public class AsyncMetadataVersionManager implements Runnable {
private String storesVersionKey = SystemStoreConstants.STORES_VERSION_KEY;
private final Logger logger = Logger.getLogger(this.getClass());
private Long currentClusterVersion;
private Long currentStoreVersion;
private final Callable<Void> storeClientThunk;
private final SystemStoreRepository systemStoreRepository;
public boolean isActive = false;
public AsyncMetadataVersionManager(SystemStoreRepository sysRepository,
Callable<Void> storeClientThunk,
String storeName) {
this.systemStoreRepository = sysRepository;
if(storeName != null) {
storesVersionKey = storeName;
}
// Get the properties object from the system store (containing versions)
Properties versionProps = MetadataVersionStoreUtils.getProperties(this.systemStoreRepository.getMetadataVersionStore());
// Initialize base cluster version to do all subsequent comparisons
this.currentClusterVersion = initializeVersion(SystemStoreConstants.CLUSTER_VERSION_KEY,
versionProps);
// Initialize base store version to do all subsequent comparisons
this.currentStoreVersion = initializeVersion(storesVersionKey, versionProps);
logger.debug("Initial cluster.xml version = " + this.currentClusterVersion);
logger.debug("Initial store '" + storeName + "' version = " + this.currentClusterVersion);
this.storeClientThunk = storeClientThunk;
this.isActive = true;
}
private Long initializeVersion(String versionKey, Properties versionProps) {
Long baseVersion = null;
try {
baseVersion = getCurrentVersion(versionKey, versionProps);
} catch(Exception e) {
logger.error("Exception while getting version for key : " + versionKey
+ " Exception : " + e);
}
if(baseVersion == null) {
baseVersion = new Long(0);
}
return baseVersion;
}
public Long getCurrentVersion(String versionKey, Properties versionProps) {
Long versionValue = null;
if (versionProps != null && versionProps.getProperty(versionKey) != null) {
versionValue = Long.parseLong(versionProps.getProperty(versionKey));
}
logger.debug("*********** For key : " + versionKey + " received value = " + versionValue);
return versionValue;
}
/*
* This method checks for any update in the version for 'versionKey'. If
* there is any change, it returns the new version. Otherwise it will return
* a null.
*/
public Long fetchNewVersion(String versionKey, Long curVersion, Properties versionProps) {
try {
Long newVersion = getCurrentVersion(versionKey, versionProps);
// If version obtained is null, the store is untouched. Continue
if(newVersion != null) {
logger.debug("MetadataVersion check => Obtained " + versionKey + " version : "
+ newVersion);
/*
* Check if the new version is greater than the current one. We
* should not re-bootstrap on a stale version.
*/
if(curVersion == null || newVersion > curVersion) {
return newVersion;
}
} else {
logger.debug("Metadata unchanged after creation ...");
}
}
// Swallow all exceptions here (we don't want to fail the client).
catch(Exception e) {
logger.debug("Could not retrieve Metadata Version. Exception : " + e);
}
return null;
}
public void run() {
logger.debug("************* AsyncMetadataVersionManger running. Checking for "
+ SystemStoreConstants.CLUSTER_VERSION_KEY + " and " + storesVersionKey
+ " *************");
try {
/*
* Get the properties object from the system store (containing
* versions)
*/
Properties versionProps = MetadataVersionStoreUtils.getProperties(this.systemStoreRepository.getMetadataVersionStore());
Long newClusterVersion = fetchNewVersion(SystemStoreConstants.CLUSTER_VERSION_KEY,
this.currentClusterVersion,
versionProps);
Long newStoreVersion = fetchNewVersion(storesVersionKey,
this.currentStoreVersion,
versionProps);
// Check if something has been updated
if((newClusterVersion != null) || (newStoreVersion != null)) {
logger.info("Metadata version mismatch detected. Re-bootstrapping!");
try {
if(newClusterVersion != null) {
logger.info("Updating cluster version");
currentClusterVersion = newClusterVersion;
}
if(newStoreVersion != null) {
logger.info("Updating store : '" + storesVersionKey + "' version");
this.currentStoreVersion = newStoreVersion;
}
this.storeClientThunk.call();
} catch(Exception e) {
logger.error("Exception occurred while invoking the rebootstrap callback.", e);
}
}
} catch(InvalidMetadataException e) {
try {
this.storeClientThunk.call();
} catch(Exception e2) {
logger.error("Exception occurred while invoking the rebootstrap callback.", e);
}
} catch(Exception e) {
if(logger.isDebugEnabled()) {
logger.debug("Could not retrieve metadata versions from the server.", e);
}
}
}
public Long getClusterMetadataVersion() {
return this.currentClusterVersion;
}
public Long getStoreMetadataVersion() {
return this.currentStoreVersion;
}
// Fetch the latest versions for cluster metadata
public void updateMetadataVersions() {
Properties versionProps = MetadataVersionStoreUtils.getProperties(this.systemStoreRepository.getMetadataVersionStore());
Long newVersion = fetchNewVersion(SystemStoreConstants.CLUSTER_VERSION_KEY,
null,
versionProps);
if(newVersion != null) {
this.currentClusterVersion = newVersion;
}
}
}