/// <summary> /// Writes the buffer to SQL and empties it. /// </summary> private void WriteData() { lock (this) { // This and the lock are to circumvent a small multithreading dillema if (this.Available) { Trace.WriteLine(String.Format(CultureInfo.InvariantCulture, "BufferRecorder::WriteData called when Available at time {0}. Returning.", DateTime.Now.Ticks)); return; } // Going to try a spin-wait just to see if we can resolve this multithreading problem while (this.writing) { Thread.Sleep(10); } writing = true; bool overflowed; do { overflowed = false; try { // Don't try to write an empty buffer if (currentIndex > 0) { #if TIMING long startTimer = DateTime.Now.Ticks; #endif // Try to detect and fix overflow before it happens to avoid the exception and hopefully improve performance. if (DBHelper.WouldOverflowStream(this.streamID, this.indices[this.currentIndex - 1].end)) { this.Overflowed(this, EventArgs.Empty); } // TODO: Find out why this gets called twice on one data set OR find a workaround. Trace.WriteLine(String.Format(CultureInfo.InvariantCulture, "BufferRecorder::WriteData doing SBAI on buffer {0}, stream {1} at {2}", number, streamID, DateTime.Now.Ticks)); DBHelper.SaveBufferAndIndices( this.streamID, this.indices, this.currentIndex, buffer); #if TIMING long takenTime = DateTime.Now.Ticks - startTimer; Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "TIMING: SBAI took {0} ms", (takenTime / Constants.TicksPerMs))); #endif } } catch (SqlException ex) { // Catch the overflow case, where we have more data than the 'int' type holds if (ex.Message.ToLower(CultureInfo.InvariantCulture).IndexOf("overflow") >= 0) { overflowed = true; // by setting this, we directly try to write the data again this.Overflowed(this, EventArgs.Empty); } // Two exceptions are seen here: // Timeouts in SQL due to taking a *really* bad performance beating // OR Constraint violation in Frame table due to unusual multithreading problem not yet solved (mentioned above) // Dont do anything, b/c failed DB ops are already event-logged. Just move on & hope that the stream is playable. } catch (InvalidOperationException ex) { // In the worst of performance cases, we run out of pooled connections and get this exception eventLog.WriteEntry(string.Format(CultureInfo.CurrentCulture, Strings.DatabaseOperationFailedError, ex.ToString()), EventLogEntryType.Error, ArchiveServiceEventLog.ID.DBOpFailed); // Again, ignore & move on, dropping the frames. } } // In the specific case where we've overflowed, try again immediately (to hopefully preempt other buffers from going out-of-order) while (overflowed); writing = false; isAvailable = true; bufferIndex = 0; currentIndex = 0; Trace.WriteLine("BufferRecorder::WriteData completed."); } }