示例#1
0
        /// <summary>
        /// Rotates the current log file, continuing into next (version) log file.
        /// This method must be recovery safe, which means a crash at any point should be recoverable.
        /// Concurrent readers must also be able to parry for concurrent rotation.
        /// Concurrent writes will not be an issue since rotation and writing contends on the same monitor.
        ///
        /// Steps during rotation are:
        /// <ol>
        /// <li>1: Increment log version, <seealso cref="LogVersionRepository.incrementAndGetVersion()"/> (also flushes the store)</li>
        /// <li>2: Flush current log</li>
        /// <li>3: Create new log file</li>
        /// <li>4: Write header</li>
        /// </ol>
        ///
        /// Recovery: what happens if crash between:
        /// <ol>
        /// <li>1-2: New log version has been set, starting the writer will create the new log file idempotently.
        /// At this point there may have been half-written transactions in the previous log version,
        /// although they haven't been considered committed and so they will be truncated from log during recovery</li>
        /// <li>2-3: New log version has been set, starting the writer will create the new log file idempotently.
        /// At this point there may be complete transactions in the previous log version which may not have been
        /// acknowledged to be committed back to the user, but will be considered committed anyway.</li>
        /// <li>3-4: New log version has been set, starting the writer will see that the new file exists and
        /// will be forgiving when trying to read the header of it, so that if it isn't complete a fresh
        /// header will be set.</li>
        /// </ol>
        ///
        /// Reading: what happens when rotation is between:
        /// <ol>
        /// <li>1-2: Reader bridge will see that there's a new version (when asking <seealso cref="LogVersionRepository"/>
        /// and try to open it. The log file doesn't exist yet though. The bridge can parry for this by catching
        /// <seealso cref="FileNotFoundException"/> and tell the reader that the stream has ended</li>
        /// <li>2-3: Same as (1-2)</li>
        /// <li>3-4: Here the new log file exists, but the header may not be fully written yet.
        /// the reader will fail when trying to read the header since it's reading it strictly and bridge
        /// catches that exception, treating it the same as if the file didn't exist.</li>
        /// </ol>
        /// </summary>
        /// <param name="currentLog"> current <seealso cref="LogVersionedStoreChannel channel"/> to flush and close. </param>
        /// <returns> the channel of the newly opened/created log file. </returns>
        /// <exception cref="IOException"> if an error regarding closing or opening log files occur. </exception>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
//ORIGINAL LINE: private org.neo4j.kernel.impl.transaction.log.PhysicalLogVersionedStoreChannel rotate(org.neo4j.kernel.impl.transaction.log.LogVersionedStoreChannel currentLog) throws java.io.IOException
        private PhysicalLogVersionedStoreChannel Rotate(LogVersionedStoreChannel currentLog)
        {
            /*
             * The store is now flushed. If we fail now the recovery code will open the
             * current log file and replay everything. That's unnecessary but totally ok.
             */
            long newLogVersion = _logVersionRepository.incrementAndGetVersion();

            /*
             * Rotation can happen at any point, although not concurrently with an append,
             * although an append may have (most likely actually) left at least some bytes left
             * in the buffer for future flushing. Flushing that buffer now makes the last appended
             * transaction complete in the log we're rotating away. Awesome.
             */
            _writer.prepareForFlush().flush();

            /*
             * The log version is now in the store, flushed and persistent. If we crash
             * now, on recovery we'll attempt to open the version we're about to create
             * (but haven't yet), discover it's not there. That will lead to creating
             * the file, setting the header and continuing.
             * We using committing transaction id as a source of last transaction id here since
             * we can have transactions that are not yet published as committed but were already stored
             * into transaction log that was just rotated.
             */
            PhysicalLogVersionedStoreChannel newLog = _logFiles.createLogChannelForVersion(newLogVersion, OpenMode.READ_WRITE, _context.committingTransactionId);

            currentLog.close();
            return(newLog);
        }
示例#2
0
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
//ORIGINAL LINE: public LogVersionedStoreChannel next(LogVersionedStoreChannel channel) throws java.io.IOException
        public override LogVersionedStoreChannel Next(LogVersionedStoreChannel channel)
        {
            PhysicalLogVersionedStoreChannel nextChannel;

            try
            {
                nextChannel = _logFiles.openForVersion(channel.Version + 1);
            }
            catch (Exception e) when(e is FileNotFoundException || e is IncompleteLogHeaderException)
            {
                // See PhysicalLogFile#rotate() for description as to why these exceptions are OK
                return(channel);
            }
            channel.close();
            return(nextChannel);
        }
示例#3
0
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
//ORIGINAL LINE: private int transactionCount() throws java.io.IOException
        private int TransactionCount()
        {
            return(AggregateLogData(version =>
            {
                int counter = 0;
                LogVersionBridge bridge = channel => channel;
                LogVersionedStoreChannel versionedStoreChannel = _files.openForVersion(version);
                using (ReadableLogChannel channel = new ReadAheadLogChannel(versionedStoreChannel, bridge, 1000))
                {
                    using (PhysicalTransactionCursor <ReadableLogChannel> physicalTransactionCursor = new PhysicalTransactionCursor <ReadableLogChannel>(channel, new VersionAwareLogEntryReader <>()))
                    {
                        while (physicalTransactionCursor.Next())
                        {
                            counter++;
                        }
                    }
                }
                return counter;
            }));
        }
示例#4
0
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
//ORIGINAL LINE: private void verifyTransactionsInLog(org.neo4j.kernel.impl.transaction.log.files.LogFiles logFiles, long fromTxId, long endTxId) throws java.io.IOException
        private void VerifyTransactionsInLog(LogFiles logFiles, long fromTxId, long endTxId)
        {
            long expectedTxId = fromTxId;
            LogVersionedStoreChannel versionedStoreChannel = logFiles.OpenForVersion(0);

            using (ReadableLogChannel channel = new ReadAheadLogChannel(versionedStoreChannel, Org.Neo4j.Kernel.impl.transaction.log.LogVersionBridge_Fields.NoMoreChannels, 1024))
            {
                using (PhysicalTransactionCursor <ReadableLogChannel> txCursor = new PhysicalTransactionCursor <ReadableLogChannel>(channel, new VersionAwareLogEntryReader <>()))
                {
                    while (txCursor.Next())
                    {
                        CommittedTransactionRepresentation tx = txCursor.Get();
                        long txId = tx.CommitEntry.TxId;

                        assertThat(expectedTxId, lessThanOrEqualTo(endTxId));
                        assertEquals(expectedTxId, txId);
                        expectedTxId++;
                    }
                }
            }
        }
示例#5
0
        /// <summary>
        /// Extracts txId from first commit entry, when starting reading at the given {@code position}.
        /// If no commit entry found in the version, the reader will continue into next version(s) up till
        /// {@code maxLogVersion} until finding one.
        /// </summary>
        /// <param name="initialPosition"> <seealso cref="LogPosition"/> to start scan from. </param>
        /// <param name="maxLogVersion"> max log version to scan. </param>
        /// <returns> value object that contains first transaction id of closes commit entry to {@code initialPosition},
        /// or <seealso cref="LogTailInformation.NO_TRANSACTION_ID"/> if not found. And failure flag that will be set to true if
        /// there was some exception during transaction log processing. </returns>
        /// <exception cref="IOException"> on channel close I/O error. </exception>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
//ORIGINAL LINE: protected ExtractedTransactionRecord extractFirstTxIdAfterPosition(org.neo4j.kernel.impl.transaction.log.LogPosition initialPosition, long maxLogVersion) throws java.io.IOException
        protected internal virtual ExtractedTransactionRecord ExtractFirstTxIdAfterPosition(LogPosition initialPosition, long maxLogVersion)
        {
            LogPosition currentPosition = initialPosition;

            while (currentPosition.LogVersion <= maxLogVersion)
            {
                LogVersionedStoreChannel storeChannel = TryOpenStoreChannel(currentPosition);
                if (storeChannel != null)
                {
                    try
                    {
                        storeChannel.Position(currentPosition.ByteOffset);
                        using (ReadAheadLogChannel logChannel = new ReadAheadLogChannel(storeChannel), LogEntryCursor cursor = new LogEntryCursor(_logEntryReader, logChannel))
                        {
                            while (cursor.Next())
                            {
                                LogEntry entry = cursor.Get();
                                if (entry is LogEntryCommit)
                                {
                                    return(new ExtractedTransactionRecord((( LogEntryCommit )entry).TxId));
                                }
                            }
                        }
                    }
                    catch (Exception t)
                    {
                        _monitor.corruptedLogFile(currentPosition.LogVersion, t);
                        return(new ExtractedTransactionRecord(true));
                    }
                    finally
                    {
                        storeChannel.close();
                    }
                }

                currentPosition = LogPosition.start(currentPosition.LogVersion + 1);
            }
            return(new ExtractedTransactionRecord());
        }
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
//ORIGINAL LINE: public java.util.List<org.neo4j.kernel.impl.transaction.log.entry.CheckPoint> find(long version) throws java.io.IOException
            public virtual IList <CheckPoint> Find(long version)
            {
                IList <CheckPoint> checkPoints = new List <CheckPoint>();

                for ( ; version >= INITIAL_LOG_VERSION && LogFiles.versionExists(version); version--)
                {
                    LogVersionedStoreChannel             channel = LogFiles.openForVersion(version);
                    ReadableClosablePositionAwareChannel recoveredDataChannel = new ReadAheadLogChannel(channel);

                    using (LogEntryCursor cursor = new LogEntryCursor(LogEntryReader, recoveredDataChannel))
                    {
                        while (cursor.Next())
                        {
                            LogEntry entry = cursor.Get();
                            if (entry is CheckPoint)
                            {
                                checkPoints.Add(entry.As());
                            }
                        }
                    }
                }
                return(checkPoints);
            }
 public PositionAwarePhysicalFlushableChannel(LogVersionedStoreChannel logVersionedStoreChannel, int bufferSize)
 {
     this._logVersionedStoreChannel = logVersionedStoreChannel;
     this._channel = new PhysicalFlushableChannel(logVersionedStoreChannel, bufferSize);
 }
 public PositionAwarePhysicalFlushableChannel(LogVersionedStoreChannel logVersionedStoreChannel)
 {
     this._logVersionedStoreChannel = logVersionedStoreChannel;
     this._channel = new PhysicalFlushableChannel(logVersionedStoreChannel);
 }
示例#9
0
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
//ORIGINAL LINE: private LogTailInformation findLogTail() throws java.io.IOException
        private LogTailInformation FindLogTail()
        {
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final long highestLogVersion = logFiles.getHighestLogVersion();
            long            highestLogVersion           = _logFiles.HighestLogVersion;
            long            version                     = highestLogVersion;
            long            versionToSearchForCommits   = highestLogVersion;
            LogEntryStart   latestStartEntry            = null;
            long            oldestStartEntryTransaction = -1;
            long            oldestVersionFound          = -1;
            LogEntryVersion latestLogEntryVersion       = null;
            bool            startRecordAfterCheckpoint  = false;
            bool            corruptedTransactionLogs    = false;

            while (version >= _logFiles.LowestLogVersion && version >= INITIAL_LOG_VERSION)
            {
                oldestVersionFound = version;
                CheckPoint latestCheckPoint = null;
                try
                {
                    using (LogVersionedStoreChannel channel = _logFiles.openForVersion(version), ReadAheadLogChannel readAheadLogChannel = new ReadAheadLogChannel(channel), LogEntryCursor cursor = new LogEntryCursor(_logEntryReader, readAheadLogChannel))
                    {
                        LogEntry entry;
                        long     maxEntryReadPosition = 0;
                        while (cursor.Next())
                        {
                            entry = cursor.Get();

                            // Collect data about latest checkpoint
                            if (entry is CheckPoint)
                            {
                                latestCheckPoint = entry.As();
                            }
                            else if (entry is LogEntryCommit)
                            {
                                if (oldestStartEntryTransaction == NoTransactionId)
                                {
                                    oldestStartEntryTransaction = (( LogEntryCommit )entry).TxId;
                                }
                            }
                            else if (entry is LogEntryStart)
                            {
                                LogEntryStart startEntry = entry.As();
                                if (version == versionToSearchForCommits)
                                {
                                    latestStartEntry = startEntry;
                                }
                                startRecordAfterCheckpoint = true;
                            }

                            // Collect data about latest entry version, only in first log file
                            if (version == versionToSearchForCommits || latestLogEntryVersion == null)
                            {
                                latestLogEntryVersion = entry.Version;
                            }
                            maxEntryReadPosition = readAheadLogChannel.Position();
                        }
                        if (HasUnreadableBytes(channel, maxEntryReadPosition))
                        {
                            corruptedTransactionLogs = true;
                        }
                    }
                }
                catch (Exception e) when(e is Exception || e is ClosedByInterruptException)
                {
                    // These should not be parsing errors
                    throw e;
                }
                catch (Exception t)
                {
                    _monitor.corruptedLogFile(version, t);
                    if (_failOnCorruptedLogFiles)
                    {
                        throwUnableToCleanRecover(t);
                    }
                    corruptedTransactionLogs = true;
                }

                if (latestCheckPoint != null)
                {
                    return(CheckpointTailInformation(highestLogVersion, latestStartEntry, oldestVersionFound, latestLogEntryVersion, latestCheckPoint, corruptedTransactionLogs));
                }

                version--;

                // if we have found no commits in the latest log, keep searching in the next one
                if (latestStartEntry == null)
                {
                    versionToSearchForCommits--;
                }
            }

            return(new LogTailInformation(corruptedTransactionLogs || startRecordAfterCheckpoint, oldestStartEntryTransaction, oldestVersionFound, highestLogVersion, latestLogEntryVersion));
        }
示例#10
0
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
//ORIGINAL LINE: private boolean hasUnreadableBytes(org.neo4j.kernel.impl.transaction.log.LogVersionedStoreChannel channel, long maxEntryReadEndPosition) throws java.io.IOException
        private bool HasUnreadableBytes(LogVersionedStoreChannel channel, long maxEntryReadEndPosition)
        {
            return(channel.position() > maxEntryReadEndPosition);
        }
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test public void databasePanicShouldHandleOutOfMemoryErrors() throws java.io.IOException, InterruptedException
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
        public virtual void DatabasePanicShouldHandleOutOfMemoryErrors()
        {
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final java.util.concurrent.CountDownLatch panicLatch = new java.util.concurrent.CountDownLatch(1);
            System.Threading.CountdownEvent panicLatch = new System.Threading.CountdownEvent(1);
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final java.util.concurrent.CountDownLatch adversaryLatch = new java.util.concurrent.CountDownLatch(1);
            System.Threading.CountdownEvent adversaryLatch = new System.Threading.CountdownEvent(1);
            OutOfMemoryAwareFileSystem      fs             = new OutOfMemoryAwareFileSystem();

            _life.add(new FileSystemLifecycleAdapter(fs));
            DatabaseHealth slowPanicDatabaseHealth = new SlowPanickingDatabaseHealth(panicLatch, adversaryLatch);
            LogFiles       logFiles = LogFilesBuilder.builder(_testDirectory.databaseLayout(), fs).withLogVersionRepository(_logVersionRepository).withTransactionIdStore(_transactionIdStore).build();

            _life.add(logFiles);
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final BatchingTransactionAppender appender = life.add(new BatchingTransactionAppender(logFiles, logRotation, transactionMetadataCache, transactionIdStore, explicitIndexTransactionOrdering, slowPanicDatabaseHealth));
            BatchingTransactionAppender appender = _life.add(new BatchingTransactionAppender(logFiles, _logRotation, _transactionMetadataCache, _transactionIdStore, _explicitIndexTransactionOrdering, slowPanicDatabaseHealth));

            _life.start();

            // Commit initial transaction
            appender.Append(Tx(), Org.Neo4j.Kernel.impl.transaction.tracing.LogAppendEvent_Fields.Null);

            // Try to commit one transaction, will fail during flush with OOM, but not actually panic
            fs.ShouldOOM = true;
            Future <long> failingTransaction = _executor.submit(() => appender.Append(Tx(), Org.Neo4j.Kernel.impl.transaction.tracing.LogAppendEvent_Fields.Null));

            panicLatch.await();

            // Try to commit one additional transaction, should fail since database has already panicked
            fs.ShouldOOM = false;
            try
            {
                appender.Append(Tx(), new LogAppendEvent_EmptyAnonymousInnerClass2(this, adversaryLatch));
                fail("Should have failed since database should have panicked");
            }
            catch (IOException e)
            {
                assertTrue(e.Message.contains("The database has encountered a critical error"));
            }

            // Check that we actually got an OutOfMemoryError
            try
            {
                failingTransaction.get();
                fail("Should have failed with OutOfMemoryError error");
            }
            catch (ExecutionException e)
            {
                assertTrue(e.InnerException is System.OutOfMemoryException);
            }

            // Check number of transactions, should only have one
            LogEntryReader <ReadableClosablePositionAwareChannel> logEntryReader = new VersionAwareLogEntryReader <ReadableClosablePositionAwareChannel>();

            assertEquals(logFiles.LowestLogVersion, logFiles.HighestLogVersion);
            long version = logFiles.HighestLogVersion;

            using (LogVersionedStoreChannel channel = logFiles.OpenForVersion(version), ReadAheadLogChannel readAheadLogChannel = new ReadAheadLogChannel(channel), LogEntryCursor cursor = new LogEntryCursor(logEntryReader, readAheadLogChannel))
            {
                LogEntry entry;
                long     numberOfTransactions = 0;
                while (cursor.Next())
                {
                    entry = cursor.Get();
                    if (entry is LogEntryCommit)
                    {
                        numberOfTransactions++;
                    }
                }
                assertEquals(1, numberOfTransactions);
            }
        }