blob: d9f1baf8ce5ac7c4d1fa46e9bf5be484ef44a9ca [file] [log] [blame]
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.je.log.entry;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import com.sleepycat.je.dbi.DatabaseId;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.dbi.ReplicatedDatabaseConfig;
import com.sleepycat.je.log.DbOpReplicationContext;
import com.sleepycat.je.log.LogEntryHeader;
import com.sleepycat.je.log.LogEntryType;
import com.sleepycat.je.log.ReplicationContext;
import com.sleepycat.je.log.VersionedWriteLoggable;
import com.sleepycat.je.tree.NameLN;
import com.sleepycat.je.txn.Txn;
import com.sleepycat.je.utilint.VLSN;
/**
* NameLNLogEntry contains all the regular LNLogEntry fields and additional
* information about the database operation which instigated the logging of
* this NameLN. This additional information is used to support replication of
* database operations in a replication group.
*
* Database operations pose a special problem for replication because unlike
* data record put and get calls, they can result in multiple log entries that
* are not all members of a single transaction. Create and truncate are the
* problem operations because they end up logging new MapLNs, and our
* implementation does not treat MapLNs as transactional. Database operations
* challenge two replication assumptions: (a) that all logical operations can
* be repeated on the client node based on the contents of a single log entry,
* and (b) that non-txnal log entries like MapLNs need not be replicated.
*
* Specifically, here's what is logged for database operations.
*
* create:
*
* 1. new NameLN_TX
* 2. new MapLN, which has the database config info.
* 3. txn commit of autocommit or user txn.
*
* rename:
*
* 1. deleted NameLN_TX
* 2. new NameLN_TX
* 3. txn commit from autocommit or user txn
*
* truncate:
*
* 1. new MapLN w/new id
* 2. modify the existing NameLN with new id (old database is deleted by
* usual commit-time processing)
* 3. txn commit from autocommit or user txn
*
* delete
*
* 1. deleted NameLN_TX (old database gets deleted by usual commit-time
* processing)
* 2. txn commit from autocommit or user txn
*
* Extra information is needed for create and truncate, which both log
* information within the MapLN. Rename and delete only log NameLNs, so they
* can be replicated on the client using the normal replication messages. The
* extra fields which follow the usual LNLogEntry fields are:
*
* operationType - the type of database operation. In a single node system,
* this is local information implicit in the code path.
* databaseConfig (optional) - For creates, database configuration info
* databaseId (optional)- For truncates, the old db id, so we know which
* MapLN to delete.
*/
public class NameLNLogEntry extends LNLogEntry<NameLN> {
/**
* The log version of the most recent format change for this entry,
* including the superclass and any changes to the format of referenced
* loggables.
*
* @see #getLastFormatChange
*/
private static final int LAST_FORMAT_CHANGE = 12;
/*
* operationType, truncateOldDbId and replicatedCreateConfig are
* logged as part of the entry.
*/
private DbOperationType operationType;
private DatabaseId truncateOldDbId;
private ReplicatedDatabaseConfig replicatedCreateConfig;
/**
* Constructor to read an entry.
*/
public NameLNLogEntry() {
super(com.sleepycat.je.tree.NameLN.class);
}
/**
* Constructor to write this entry.
*/
public NameLNLogEntry(
LogEntryType entryType,
DatabaseId dbId,
Txn txn,
long abortLsn,
boolean abortKD,
byte[] key,
NameLN nameLN,
int priorSize,
long priorLsn,
ReplicationContext repContext) {
super(
entryType, dbId, txn,
abortLsn, abortKD,
null/*abortKey*/, null/*abortData*/,
VLSN.NULL_VLSN_SEQUENCE/*abortVLSN*/,
0 /*abortExpiration*/, false /*abortExpirationInHours*/,
key, nameLN, false/*newEmbeddedLN*/,
0 /*expiration*/, false /*expirationInHours*/,
priorSize, priorLsn);
ReplicationContext operationContext = repContext;
operationType = repContext.getDbOperationType();
if (DbOperationType.isWriteConfigType(operationType)) {
replicatedCreateConfig =
((DbOpReplicationContext) operationContext).getCreateConfig();
}
if (operationType == DbOperationType.TRUNCATE) {
truncateOldDbId =
((DbOpReplicationContext) operationContext).getTruncateOldDbId();
}
}
/**
* Extends its super class to read in database operation information.
*/
@Override
public void readEntry(EnvironmentImpl envImpl,
LogEntryHeader header,
ByteBuffer entryBuffer) {
readBaseLNEntry(envImpl, header, entryBuffer,
false /*keyIsLastSerializedField*/);
/*
* The NameLNLogEntry was introduced in version 6. Before, a LNLogEntry
* was used for NameLNs, and there is no extra information in the log
* entry.
*/
int version = header.getVersion();
if (version >= 6) {
operationType = DbOperationType.readTypeFromLog(entryBuffer,
version);
if (DbOperationType.isWriteConfigType(operationType)) {
replicatedCreateConfig = new ReplicatedDatabaseConfig();
replicatedCreateConfig.readFromLog(entryBuffer, version);
}
if (operationType == DbOperationType.TRUNCATE) {
truncateOldDbId = new DatabaseId();
truncateOldDbId.readFromLog(entryBuffer, version);
}
} else {
operationType = DbOperationType.NONE;
}
}
/**
* Extends its super class to dump database operation information.
*/
@Override
public StringBuilder dumpEntry(StringBuilder sb, boolean verbose) {
super.dumpEntry(sb, verbose);
operationType.dumpLog(sb, verbose);
if (replicatedCreateConfig != null ) {
replicatedCreateConfig.dumpLog(sb, verbose);
}
if (truncateOldDbId != null) {
truncateOldDbId.dumpLog(sb, verbose);
}
return sb;
}
@Override
public int getLastFormatChange() {
return Math.max(LAST_FORMAT_CHANGE, super.getLastFormatChange());
}
@Override
public Collection<VersionedWriteLoggable> getEmbeddedLoggables() {
final Collection<VersionedWriteLoggable> list =
new ArrayList<>(super.getEmbeddedLoggables());
list.addAll(Arrays.asList(
new NameLN(), DbOperationType.NONE,
new ReplicatedDatabaseConfig()));
return list;
}
@Override
public int getSize(final int logVersion, final boolean forReplication) {
int size = getBaseLNEntrySize(
logVersion, false /*keyIsLastSerializedField*/,
forReplication);
size += operationType.getLogSize(logVersion, forReplication);
if (DbOperationType.isWriteConfigType(operationType)) {
size += replicatedCreateConfig.getLogSize(
logVersion, forReplication);
}
if (operationType == DbOperationType.TRUNCATE) {
size += truncateOldDbId.getLogSize(logVersion, forReplication);
}
return size;
}
@Override
public void writeEntry(final ByteBuffer destBuffer,
final int logVersion,
final boolean forReplication) {
writeBaseLNEntry(
destBuffer, logVersion,
false /*keyIsLastSerializedField*/, forReplication);
operationType.writeToLog(destBuffer, logVersion, forReplication);
if (DbOperationType.isWriteConfigType(operationType)) {
replicatedCreateConfig.writeToLog(
destBuffer, logVersion, forReplication);
}
if (operationType == DbOperationType.TRUNCATE) {
truncateOldDbId.writeToLog(destBuffer, logVersion, forReplication);
}
}
@Override
public boolean logicalEquals(LogEntry other) {
if (!super.logicalEquals(other))
return false;
NameLNLogEntry otherEntry = (NameLNLogEntry) other;
if (!operationType.logicalEquals(otherEntry.operationType)) {
return false;
}
if ((truncateOldDbId != null) &&
(!truncateOldDbId.logicalEquals(otherEntry.truncateOldDbId))) {
return false;
}
if (replicatedCreateConfig != null) {
if (!replicatedCreateConfig.logicalEquals
(otherEntry.replicatedCreateConfig))
return false;
}
return true;
}
public DbOperationType getOperationType() {
return operationType;
}
public ReplicatedDatabaseConfig getReplicatedCreateConfig() {
return replicatedCreateConfig;
}
public DatabaseId getTruncateOldDbId() {
return truncateOldDbId;
}
@Override
public void dumpRep(StringBuilder sb) {
super.dumpRep(sb);
sb.append(" dbop=").append(operationType);
}
}