package com.sleepycat.je.utilint;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DbInternal;
import com.sleepycat.je.Environment;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.dbi.DatabaseImpl;
import com.sleepycat.je.dbi.DbConfigManager;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.log.FileManager;
import com.sleepycat.je.log.LastFileReader;
import com.sleepycat.je.log.LogEntryType;
import com.sleepycat.je.log.ScavengerFileReader;
import com.sleepycat.je.log.entry.LNLogEntry;
import com.sleepycat.je.log.entry.LogEntry;
import com.sleepycat.je.tree.LN;
import com.sleepycat.je.tree.MapLN;
import com.sleepycat.je.tree.NameLN;
import com.sleepycat.je.util.DbDump;
import de.ovgu.cide.jakutil.*;
public class DbScavenger extends DbDump {
private static final int FLUSH_INTERVAL=100;
private int readBufferSize;
private EnvironmentImpl envImpl;
private BitMap committedTxnIdsSeen;
private BitMap nodeIdsSeen;
private Map dbIdToName;
private Map dbIdToDupSort;
private Map dbIdToOutputStream;
private boolean dumpCorruptedBounds=false;
private int flushCounter=0;
private long lastTime;
public DbScavenger( Environment env, PrintStream outputFile, String outputDirectory, boolean formatUsingPrintable, boolean doAggressiveScavengerRun, boolean verbose){
super(env,null,outputFile,outputDirectory,formatUsingPrintable);
this.doAggressiveScavengerRun=doAggressiveScavengerRun;
this.dbIdToName=new HashMap();
this.dbIdToDupSort=new HashMap();
this.dbIdToOutputStream=new HashMap();
this.verbose=verbose;
}
/**
* Set to true if corrupted boundaries should be dumped out.
*/
public void setDumpCorruptedBounds( boolean dumpCorruptedBounds){
this.dumpCorruptedBounds=dumpCorruptedBounds;
}
public void dump() throws IOException, DatabaseException {
openEnv(false);
envImpl=DbInternal.envGetEnvironmentImpl(env);
DbConfigManager cm=envImpl.getConfigManager();
try {
readBufferSize=cm.getInt(EnvironmentParams.LOG_ITERATOR_READ_SIZE);
}
catch ( DatabaseException DBE) {
readBufferSize=8192;
}
LastFileReader reader=new LastFileReader(envImpl,readBufferSize);
while (reader.readNextEntry()) {
}
long lastUsedLsn=reader.getLastValidLsn();
long nextAvailableLsn=reader.getEndOfLog();
envImpl.getFileManager().setLastPosition(nextAvailableLsn,lastUsedLsn,reader.getPrevOffset());
try {
if (verbose) {
System.out.println("Pass 1: " + new Date());
}
scavengeDbTree(lastUsedLsn,nextAvailableLsn);
if (verbose) {
System.out.println("Pass 2: " + new Date());
}
scavenge(lastUsedLsn,nextAvailableLsn);
if (verbose) {
System.out.println("End: " + new Date());
}
}
finally {
closeOutputStreams();
}
}
private void scavengeDbTree( long lastUsedLsn, long nextAvailableLsn) throws IOException, DatabaseException {
committedTxnIdsSeen=new BitMap();
nodeIdsSeen=new BitMap();
final ScavengerFileReader scavengerReader=new ScavengerFileReader(envImpl,readBufferSize,lastUsedLsn,DbLsn.NULL_LSN,nextAvailableLsn){
protected void processEntryCallback( LogEntry entry, LogEntryType entryType) throws DatabaseException {
processDbTreeEntry(entry,entryType);
}
}
;
scavengerReader.setTargetType(LogEntryType.LOG_MAPLN_TRANSACTIONAL);
scavengerReader.setTargetType(LogEntryType.LOG_MAPLN);
scavengerReader.setTargetType(LogEntryType.LOG_NAMELN_TRANSACTIONAL);
scavengerReader.setTargetType(LogEntryType.LOG_NAMELN);
scavengerReader.setTargetType(LogEntryType.LOG_TXN_COMMIT);
scavengerReader.setTargetType(LogEntryType.LOG_TXN_ABORT);
lastTime=System.currentTimeMillis();
long fileNum=-1;
while (scavengerReader.readNextEntry()) {
fileNum=reportProgress(fileNum,scavengerReader.getLastLsn());
}
}
private long reportProgress( long fileNum, long lastLsn){
long currentFile=DbLsn.getFileNumber(lastLsn);
if (verbose) {
if (currentFile != fileNum) {
long now=System.currentTimeMillis();
System.out.println("processing file " + FileManager.getFileName(currentFile,".jdb ") + (now - lastTime)+ " ms");
lastTime=now;
}
}
return currentFile;
}
private boolean checkProcessEntry( LogEntry entry, LogEntryType entryType, boolean pass2){
boolean isTransactional=entry.isTransactional();
if (isTransactional) {
long txnId=entry.getTransactionId();
if (entryType.equals(LogEntryType.LOG_TXN_COMMIT)) {
committedTxnIdsSeen.set(txnId);
return false;
}
if (entryType.equals(LogEntryType.LOG_TXN_ABORT)) {
return false;
}
if (!committedTxnIdsSeen.get(txnId)) {
return false;
}
}
if (entry instanceof LNLogEntry) {
LNLogEntry lnEntry=(LNLogEntry)entry;
LN ln=lnEntry.getLN();
long nodeId=ln.getNodeId();
boolean isDelDupLN=entryType.equals(LogEntryType.LOG_DEL_DUPLN_TRANSACTIONAL) || entryType.equals(LogEntryType.LOG_DEL_DUPLN);
if (pass2 && doAggressiveScavengerRun) {
return !isDelDupLN;
}
if (nodeIdsSeen.get(nodeId)) {
return false;
}
else {
nodeIdsSeen.set(nodeId);
if (isDelDupLN) {
return false;
}
else {
return true;
}
}
}
return false;
}
private void processDbTreeEntry( LogEntry entry, LogEntryType entryType) throws DatabaseException {
boolean processThisEntry=checkProcessEntry(entry,entryType,false);
if (processThisEntry && (entry instanceof LNLogEntry)) {
LNLogEntry lnEntry=(LNLogEntry)entry;
LN ln=lnEntry.getLN();
if (ln instanceof NameLN) {
String name=new String(lnEntry.getKey());
Integer dbId=new Integer(((NameLN)ln).getId().getId());
if (dbIdToName.containsKey(dbId) && !((String)dbIdToName.get(dbId)).equals(name)) {
throw new DatabaseException("Already name mapped for dbId: " + dbId + " changed from "+ (String)dbIdToName.get(dbId)+ " to "+ name);
}
else {
dbIdToName.put(dbId,name);
}
}
if (ln instanceof MapLN) {
DatabaseImpl db=((MapLN)ln).getDatabase();
Integer dbId=new Integer(db.getId().getId());
Boolean dupSort=Boolean.valueOf(db.getSortedDuplicates());
if (dbIdToDupSort.containsKey(dbId)) {
throw new DatabaseException("Already saw dupSort entry for dbId: " + dbId);
}
else {
dbIdToDupSort.put(dbId,dupSort);
}
}
}
}
private void scavenge( long lastUsedLsn, long nextAvailableLsn) throws IOException, DatabaseException {
final ScavengerFileReader scavengerReader=new ScavengerFileReader(envImpl,readBufferSize,lastUsedLsn,DbLsn.NULL_LSN,nextAvailableLsn){
protected void processEntryCallback( LogEntry entry, LogEntryType entryType) throws DatabaseException {
processRegularEntry(entry,entryType);
}
}
;
scavengerReader.setTargetType(LogEntryType.LOG_LN_TRANSACTIONAL);
scavengerReader.setTargetType(LogEntryType.LOG_LN);
scavengerReader.setTargetType(LogEntryType.LOG_DEL_DUPLN_TRANSACTIONAL);
scavengerReader.setTargetType(LogEntryType.LOG_DEL_DUPLN);
scavengerReader.setDumpCorruptedBounds(dumpCorruptedBounds);
long progressFileNum=-1;
while (scavengerReader.readNextEntry()) {
progressFileNum=reportProgress(progressFileNum,scavengerReader.getLastLsn());
}
}
private void processRegularEntry( LogEntry entry, LogEntryType entryType) throws DatabaseException {
boolean processThisEntry=checkProcessEntry(entry,entryType,true);
if (processThisEntry) {
LNLogEntry lnEntry=(LNLogEntry)entry;
Integer dbId=new Integer(lnEntry.getDbId().getId());
PrintStream out=getOutputStream(dbId);
LN ln=lnEntry.getLN();
byte[] keyData=lnEntry.getKey();
byte[] data=ln.getData();
if (data != null) {
dumpOne(out,keyData,formatUsingPrintable);
dumpOne(out,data,formatUsingPrintable);
if ((++flushCounter % FLUSH_INTERVAL) == 0) {
out.flush();
flushCounter=0;
}
}
}
}
private PrintStream getOutputStream( Integer dbId) throws DatabaseException {
try {
PrintStream ret=(PrintStream)dbIdToOutputStream.get(dbId);
if (ret != null) {
return ret;
}
String name=(String)dbIdToName.get(dbId);
if (name == null) {
name="db" + dbId;
}
File file=new File(outputDirectory,name + ".dump");
ret=new PrintStream(new FileOutputStream(file),false);
dbIdToOutputStream.put(dbId,ret);
Boolean dupSort=(Boolean)dbIdToDupSort.get(dbId);
if (dupSort == null) {
dupSort=Boolean.valueOf(false);
}
printHeader(ret,dupSort.booleanValue(),formatUsingPrintable);
return ret;
}
catch ( IOException IOE) {
throw new DatabaseException(IOE);
}
}
private void closeOutputStreams(){
Iterator iter=dbIdToOutputStream.values().iterator();
while (iter.hasNext()) {
PrintStream s=(PrintStream)iter.next();
s.println("DATA=END");
s.close();
}
}
}