001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: BINDelta.java,v 1.44.2.4 2008/01/07 15:14:16 cwl Exp $
007: */
008:
009: package com.sleepycat.je.tree;
010:
011: import java.nio.ByteBuffer;
012: import java.util.ArrayList;
013: import java.util.List;
014:
015: import com.sleepycat.je.DatabaseException;
016: import com.sleepycat.je.dbi.DatabaseId;
017: import com.sleepycat.je.dbi.DatabaseImpl;
018: import com.sleepycat.je.dbi.EnvironmentImpl;
019: import com.sleepycat.je.log.LogEntryType;
020: import com.sleepycat.je.log.LogException;
021: import com.sleepycat.je.log.LogUtils;
022: import com.sleepycat.je.log.Loggable;
023: import com.sleepycat.je.utilint.DbLsn;
024:
025: /**
026: * BINDelta contains the information needed to create a partial (delta) BIN log
027: * entry. It also knows how to combine a full BIN log entry and a delta to
028: * generate a new BIN.
029: */
030: public class BINDelta implements Loggable {
031:
032: private DatabaseId dbId; // owning db for this bin.
033: private long lastFullLsn; // location of last full version
034: private List deltas; // list of key/action changes
035: private LogEntryType logEntryType; // type of log entry to use
036:
037: // when writing to the log.
038:
039: /**
040: * Read a BIN and create the deltas.
041: */
042: public BINDelta(BIN bin) {
043: lastFullLsn = bin.getLastFullVersion();
044: dbId = bin.getDatabaseId();
045: deltas = new ArrayList();
046: logEntryType = bin.getBINDeltaType();
047:
048: /*
049: * Save every entry that has been modified since the last full version.
050: * Note that we must rely on the dirty bit, and we can't infer any
051: * dirtiness by comparing the last full version LSN and the child
052: * reference LSN. That's because the ChildReference LSN may be earlier
053: * than the full version LSN because of aborts.
054: */
055: for (int i = 0; i < bin.getNEntries(); i++) {
056: if (bin.isDirty(i)) {
057: deltas.add(new DeltaInfo(bin.getKey(i), bin.getLsn(i),
058: bin.getState(i)));
059: }
060: }
061: }
062:
063: /**
064: * For instantiating from the log.
065: */
066: public BINDelta() {
067: dbId = new DatabaseId();
068: lastFullLsn = DbLsn.NULL_LSN;
069: deltas = new ArrayList();
070: }
071:
072: /**
073: * @return a count of deltas for this BIN.
074: */
075: int getNumDeltas() {
076: return deltas.size();
077: }
078:
079: /**
080: * @return the dbId for this BIN.
081: */
082: public DatabaseId getDbId() {
083: return dbId;
084: }
085:
086: /**
087: * @return the last full version of this BIN
088: */
089: public long getLastFullLsn() {
090: return lastFullLsn;
091: }
092:
093: /**
094: * Create a BIN by starting with the full version and applying the deltas.
095: */
096: public BIN reconstituteBIN(EnvironmentImpl env)
097: throws DatabaseException {
098:
099: /* Get the last full version of this BIN. */
100: BIN fullBIN = (BIN) env.getLogManager().get(lastFullLsn);
101: DatabaseImpl db = env.getDbMapTree().getDb(dbId);
102: try {
103:
104: /*
105: * In effect, call fullBIN.postFetchInit(db) here. But we don't
106: * want to do that since it will put fullBIN on the INList. Since
107: * this is either recovery or during the Cleaner run, we don't want
108: * it on the INList.
109: */
110: fullBIN.setDatabase(db);
111: fullBIN.setLastFullLsn(lastFullLsn);
112:
113: /* Process each delta. */
114: fullBIN.latch();
115: for (int i = 0; i < deltas.size(); i++) {
116: DeltaInfo info = (DeltaInfo) deltas.get(i);
117:
118: /*
119: * The BINDelta holds the authoritative version of each entry.
120: * In all cases, its entry should supercede the entry in the
121: * full BIN. This is true even if the BIN Delta's entry is
122: * knownDeleted or if the full BIN's version is knownDeleted.
123: * Therefore we use the flavor of findEntry that will return a
124: * knownDeleted entry if the entry key matches (i.e. true,
125: * false) but still indicates exact matches with the return
126: * index. findEntry only returns deleted entries if third arg
127: * is false, but we still need to know if it's an exact match
128: * or not so indicateExact is true.
129: */
130: int foundIndex = fullBIN.findEntry(info.getKey(), true,
131: false);
132: if (foundIndex >= 0
133: && (foundIndex & IN.EXACT_MATCH) != 0) {
134: foundIndex &= ~IN.EXACT_MATCH;
135:
136: /*
137: * The entry exists in the full version, update it with the
138: * delta info.
139: */
140: if (info.isKnownDeleted()) {
141: fullBIN.setKnownDeleted(foundIndex);
142: } else {
143: fullBIN.updateEntry(foundIndex, info.getLsn(),
144: info.getState());
145: }
146: } else {
147:
148: /*
149: * The entry doesn't exist, add a new entry from the delta.
150: */
151: if (!info.isKnownDeleted()) {
152: ChildReference entry = new ChildReference(null,
153: info.getKey(), info.getLsn(), info
154: .getState());
155: boolean insertOk = fullBIN.insertEntry(entry);
156: assert insertOk;
157: }
158: }
159: }
160: } finally {
161: env.releaseDb(db);
162: }
163:
164: /*
165: * Reset the generation to 0, all this manipulation might have driven
166: * it up.
167: */
168: fullBIN.setGeneration(0);
169: fullBIN.releaseLatch();
170: return fullBIN;
171: }
172:
173: /*
174: * Logging support
175: */
176:
177: /*
178: * @see Loggable#getLogSize()
179: */
180: public int getLogSize() {
181: int size = dbId.getLogSize() + // database id
182: LogUtils.LONG_BYTES + // last version
183: LogUtils.INT_BYTES; // num deltas
184:
185: for (int i = 0; i < deltas.size(); i++) { // deltas
186: DeltaInfo info = (DeltaInfo) deltas.get(i);
187: size += info.getLogSize();
188: }
189:
190: return size;
191: }
192:
193: /*
194: * @see Loggable#writeToLog
195: */
196: public void writeToLog(ByteBuffer logBuffer) {
197: dbId.writeToLog(logBuffer); // database id
198: LogUtils.writeLong(logBuffer, lastFullLsn); // last version
199: LogUtils.writeInt(logBuffer, deltas.size()); // num deltas
200:
201: for (int i = 0; i < deltas.size(); i++) { // deltas
202: DeltaInfo info = (DeltaInfo) deltas.get(i);
203: info.writeToLog(logBuffer);
204: }
205: }
206:
207: /*
208: * @see Loggable#readFromLog()
209: */
210: public void readFromLog(ByteBuffer itemBuffer, byte entryTypeVersion)
211: throws LogException {
212:
213: dbId.readFromLog(itemBuffer, entryTypeVersion); // database id
214: lastFullLsn = LogUtils.readLong(itemBuffer); // last version
215: int numDeltas = LogUtils.readInt(itemBuffer);
216:
217: for (int i = 0; i < numDeltas; i++) { // deltas
218: DeltaInfo info = new DeltaInfo();
219: info.readFromLog(itemBuffer, entryTypeVersion);
220: deltas.add(info);
221: }
222: }
223:
224: /*
225: * @see Loggable#dumpLog
226: */
227: public void dumpLog(StringBuffer sb, boolean verbose) {
228: dbId.dumpLog(sb, verbose);
229: sb.append("<lastFullLsn>");
230: sb.append(DbLsn.toString(lastFullLsn));
231: sb.append("</lastFullLsn>");
232: sb.append("<deltas size=\"").append(deltas.size()).append(
233: "\"/>");
234: for (int i = 0; i < deltas.size(); i++) { // deltas
235: DeltaInfo info = (DeltaInfo) deltas.get(i);
236: info.dumpLog(sb, verbose);
237: }
238: }
239:
240: /**
241: * @see Loggable#getTransactionId
242: */
243: public long getTransactionId() {
244: return 0;
245: }
246: }
|