internal void RollbackTransactionChange(ITableEventRegistry registry) { lock (this) { // ASSERT: Can't do this is source is Read only. if (IsReadOnly) { throw new InvalidOperationException("Can't rollback transaction journal, table is Read only."); } // Any rows added in the journal are marked as committed deleted and the // journal is then discarded. try { // Mark all rows in the data_store as appropriate to the changes. foreach (var tableEvent in registry) { if (tableEvent is TableRowEvent) { var rowEvent = (TableRowEvent)tableEvent; if (rowEvent.EventType == TableRowEventType.Add) { var oldState = WriteRecordState(rowEvent.RowNumber, RecordState.CommittedRemoved); if (oldState != RecordState.Uncommitted) { WriteRecordState(rowEvent.RowNumber, oldState); throw new InvalidOperationException($"Record {rowEvent.RowNumber} was not in an uncommitted state."); } GC.DeleteRow(rowEvent.RowNumber); } } } } catch (IOException e) { throw new InvalidOperationException("IO Error: " + e.Message, e); } } }
internal void CommitTransactionChange(long commitId, ITableEventRegistry change, IRowIndexSet indexSet) { lock (this) { // ASSERT: Can't do this if source is Read only. if (IsReadOnly) { throw new InvalidOperationException("Can't commit transaction journal, table is Read only."); } // CHECK! // TODO: change.CommitId = commitId; try { // Add this registry to the multi version table indices log Registries.AddRegistry(change); // Write the modified index set to the index store // (Updates the index file) CommitIndexSet(indexSet); // Update the state of the committed added data to the file system. // (Updates data to the allocation file) // // ISSUE: This can add up to a lot of changes to the allocation file and // the runtime could potentially be terminated in the middle of // the update. If an interruption happens the allocation information // may be incorrectly flagged. The type of corruption this would // result in would be; // + From an 'update' the updated record may disappear. // + From a 'delete' the deleted record may not delete. // + From an 'insert' the inserted record may not insert. // // Note, the possibility of this type of corruption occuring has been // minimized as best as possible given the current architecture. // Also note that is not possible for a table file to become corrupted // beyond recovery from this issue. foreach (var entry in change) { if (entry is TableRowEvent) { var rowEvent = (TableRowEvent)entry; var rowIndex = rowEvent.RowNumber; if (rowEvent.EventType == TableRowEventType.Add) { // Record commit added var oldType = WriteRecordState(rowIndex, RecordState.CommittedAdded); // Check the record was in an uncommitted state before we changed // it. if (oldType != RecordState.Uncommitted) { WriteRecordState(rowIndex, oldType); throw new InvalidOperationException($"Record {rowIndex} of table {TableName} was not in an uncommitted state!"); } } else if (rowEvent.EventType == TableRowEventType.Remove) { // Record commit removed var oldType = WriteRecordState(rowIndex, RecordState.CommittedRemoved); // Check the record was in an added state before we removed it. if (oldType != RecordState.CommittedAdded) { WriteRecordState(rowIndex, oldType); throw new InvalidOperationException($"Record {rowIndex} of table {TableName} was not in an added state!"); } // Notify collector that this row has been marked as deleted. GC.DeleteRow(rowIndex); } } } } catch (IOException e) { throw new InvalidOperationException("IO Error: " + e.Message, e); } } }