public void UpdateBeingCollectedProperly() { using (FileDeleter fd = new FileDeleter(Extensions.GetTempDBFile())) { Database db = new Database(new Context(fd.Fi)); using (SQLiteConnection conn = db.Connection) { conn.Open(); Initializer init = new Initializer(null); init.Initialize(db); Tuple <List <DeviceInfo>, DateTimeOffset> devices = LoadDevices(db, conn); TimeSpan one_second = TimeSpan.FromSeconds(1); DBCollectionTimeRetriever retriever = new DBCollectionTimeRetriever(conn); foreach (long collector_id in retriever.AllIDs) { string sql = $"SELECT LastCollectionAttempt, LastCollectedAt, NextCollectionTime, CurrentlyBeingCollected, FrequencyInMinutes FROM Collectors WHERE CollectorID = {collector_id}"; using (SQLiteCommand command = new SQLiteCommand(sql, conn)) using (SQLiteDataReader reader = command.ExecuteReader()) { if (reader.Read()) { // Before being collected, the CurrentlyBeingCollected column should have 0 Assert.True(reader.IsDBNull(0)); Assert.True(reader.IsDBNull(1)); Assert.True(reader.IsDBNull(2)); Assert.False(reader.IsDBNull(3)); Assert.Equal(0, reader.GetInt32(3)); Assert.False(reader.IsDBNull(4)); } } using (BeingCollected bc = new BeingCollected(collector_id, conn)) { using (SQLiteCommand command = new SQLiteCommand(sql, conn)) using (SQLiteDataReader reader = command.ExecuteReader()) { if (reader.Read()) { // Now that it's being collected, the LastCollectionAttempt and NextCollectionTime columns // should have something there, and the CurrentlyBeingCollected column should have 1 Assert.False(reader.IsDBNull(0)); Assert.True(reader.IsDBNull(1)); Assert.False(reader.IsDBNull(2)); Assert.False(reader.IsDBNull(3)); Assert.Equal(1, reader.GetInt32(3)); if (DateTimeOffset.TryParse(reader.GetString(0), out DateTimeOffset lca)) { // Make sure it just happened...let's say within 1 second of now DateTimeOffset now = DateTimeOffset.Now; DateTimeOffset lower = now - one_second; Assert.InRange(lca, lower, now); // And make sure NextCollectionTime is correct--lets just see if the time is // in the future. int frequency = reader.GetInt32(4); if (frequency > 0 && DateTimeOffset.TryParse(reader.GetString(2), out DateTimeOffset next)) { Assert.True(next >= now); } } else { Assert.True(false); } } } } using (SQLiteCommand command = new SQLiteCommand(sql, conn)) using (SQLiteDataReader reader = command.ExecuteReader()) { if (reader.Read()) { // And after being collected, the CurrentlyBeingCollected column should have 0 again Assert.False(reader.IsDBNull(0)); Assert.True(reader.IsDBNull(1)); Assert.False(reader.IsDBNull(2)); Assert.False(reader.IsDBNull(3)); Assert.Equal(0, reader.GetInt32(3)); } } // The LastCollectedAt column should remain NULL because it's set when the data is stored and // we didn't store anything here. } // Now make sure none of them show as being collected foreach (long collector_id in retriever.AllIDs) { string sql = $"SELECT CurrentlyBeingCollected FROM Collectors WHERE CollectorID = {collector_id}"; using (SQLiteCommand command = new SQLiteCommand(sql, conn)) using (SQLiteDataReader reader = command.ExecuteReader()) { if (reader.Read()) { Assert.Equal(0, reader.GetInt32(0)); } } } // And make sure that if an exception occurs while marking it as being collected that it shows as // no longer being collected foreach (long collector_id in retriever.AllIDs) { string sql = $"SELECT CurrentlyBeingCollected FROM Collectors WHERE CollectorID = {collector_id}"; try { using (BeingCollected bc = new BeingCollected(collector_id, conn)) { using (SQLiteCommand command = new SQLiteCommand(sql, conn)) using (SQLiteDataReader reader = command.ExecuteReader()) { if (reader.Read()) { Assert.Equal(1, reader.GetInt32(0)); } } throw new Exception($"Exception with collector_id {collector_id}"); } } catch (Exception) { } using (SQLiteCommand command = new SQLiteCommand(sql, conn)) using (SQLiteDataReader reader = command.ExecuteReader()) { if (reader.Read()) { Assert.Equal(0, reader.GetInt32(0)); } } } } } }
protected void ThreadFunc() { try { int max_collections_per_pass = 10; logging.EventLog elog = new ApplicationEventLog(); Database db = new Database(); DataStorage storage = new DataStorage(); m_interpreters.ForEach(i => storage.AddInterpreter(i)); while (GlobalIsRunning.IsRunning) { int collector_count = 0; using (SQLiteConnection conn = db.Connection) { conn.Open(); CheckForConfigurationChanges(storage, conn); // Used to hold which collector was doing its thing if/when an exception occurs. // It's used in the exception handler. string collector_name = string.Empty; try { DBCollectionTimeRetriever retriever = new DBCollectionTimeRetriever(conn); // Gets the list of things that need to be collected right now. They'll // be in the order they should be collected. List <DataCollector> collectors = m_system_device.GetCollectors(retriever); collector_count = collectors.Count; // Limit this to the top 10 or so while (collectors.Count > max_collections_per_pass) { collectors.RemoveAt(collectors.Count - 1); } foreach (DataCollector collector in collectors) { collector_name = collector.Context.Name; //elog.LogInformation($"Collecting {collector_name}"); Stopwatch watch = Stopwatch.StartNew(); // Records that the collector is being collected, updates the next collection // time, and records when the collection attempt was started. When it's destroyed // when exiting the using, it records that it is no longer being collected. // // This was done so even if an exception occurs within Acquire(), the flag // that the collector is being collected will be cleared. using (BeingCollected bc = new BeingCollected(collector.Context.ID.ID, conn)) { collector.Acquire(); } //long elapsed_ms = watch.ElapsedMilliseconds; //if(elapsed_ms > 500) // elog.LogInformation($"Collecting {collector_name} took {elapsed_ms} ms"); if (GlobalIsRunning.IsRunning == false) { break; // out of the foreach loop } } } catch (Exception e) { elog.LogError($"Exception from within {collector_name}"); elog.Log(e); } // Will write the daily file when it's the right time to do so; otherwise, it does nothing. if (GlobalIsRunning.IsRunning) { m_daily_file_writer.DoWrite(conn); } if (GlobalIsRunning.IsRunning) { m_db_cleaner.CleanOldData(m_days_to_keep, conn); } } // Deletes any old daily files if (GlobalIsRunning.IsRunning) { m_daily_file_cleaner.DoClean(); } // Delete any old log files too, if it's time to do so. if (GlobalIsRunning.IsRunning) { LogManager.CleanOldData(); } // And make sure we update our logging if it changed if (GlobalIsRunning.IsRunning) { LogManager.CheckConfiguration(); } // m_shutdown will be reset when the thread starts, and set when it's time to // stop the thread. So this will wait if this event hasn't been // set, but will return immediately if it has been set. // // If there's still more data to collect let's make another run right away if (GlobalIsRunning.IsRunning && collector_count < max_collections_per_pass) { m_shutdown.WaitOne(TimeSpan.FromSeconds(10)); } } m_interpreters.ForEach(i => storage.RemoveInterpreter(i)); } catch (Exception e) { logging.EventLog elog = new ApplicationEventLog(); elog.Log(e); m_shutdown.Reset(); m_thread.Abort(); m_thread = null; } }