[tor-commits] [ooni-probe/master] Add more unittests for the scheduler

art at torproject.org art at torproject.org
Mon Sep 19 12:14:25 UTC 2016


commit 4865ade8dc39d924e6a14e00c9b5a9e741f50abc
Author: Arturo Filastò <arturo at filasto.net>
Date:   Mon Sep 12 14:24:41 2016 +0200

    Add more unittests for the scheduler
    
    * Make the logic for triggering scheduled deck tasks more robust
---
 ooni/agent/scheduler.py      | 49 ++++++++++++++++++++++++++++++++++----------
 ooni/tests/test_scheduler.py | 28 ++++++++++++++++++++++++-
 2 files changed, 65 insertions(+), 12 deletions(-)

diff --git a/ooni/agent/scheduler.py b/ooni/agent/scheduler.py
index 98b395b..4002369 100644
--- a/ooni/agent/scheduler.py
+++ b/ooni/agent/scheduler.py
@@ -1,6 +1,7 @@
 import os
 import errno
 
+from hashlib import md5
 from datetime import datetime
 
 from twisted.application import service
@@ -41,6 +42,10 @@ class FileSystemlockAndMutex(object):
         self._fs_lock.unlock()
         self._mutex.release()
 
+    @property
+    def locked(self):
+        return self._mutex.locked or self._fs_lock.locked
+
 # We use this date to indicate that the scheduled task has never run.
 # Easter egg, try to see what is special about this date :)?
 CANARY_DATE = datetime(1957, 8, 4)
@@ -68,6 +73,9 @@ class ScheduledTask(object):
             FilePath(scheduler_directory).child(self.identifier + ".lock").path
         )
 
+    def cancel(self):
+        self._last_run_lock.release()
+
     @property
     def should_run(self):
         current_time = datetime.utcnow()
@@ -233,7 +241,8 @@ class RunDeck(ScheduledTask):
     def __init__(self, director, deck_id, schedule):
         self.deck_id = deck_id
         self.director = director
-        identifier = 'run-deck-' + deck_id
+        # We use as identifier also the schedule time
+        identifier = 'run-deck-' + deck_id + '-' + md5(schedule).hexdigest()
         super(RunDeck, self).__init__(schedule, identifier)
 
     @defer.inlineCallbacks
@@ -316,21 +325,39 @@ class SchedulerService(service.MultiService):
     def schedule(self, task):
         self._scheduled_tasks.append(task)
 
-    def refresh_deck_list(self):
-        # Deletes all the RunDeck tasks and reschedules only the ones that
-        # are enabled.
-        for scheduled_task in self._scheduled_tasks[:]:
-            if isinstance(scheduled_task, RunDeck):
-                self._scheduled_tasks.remove(scheduled_task)
+    def unschedule(self, task):
+        # We first cancel the task so the run lock is deleted
+        task.cancel()
+        self._scheduled_tasks.remove(task)
 
-        if not config.is_initialized():
-            # Disable scheduling measurements if we are not initialized.
-            return
+    def refresh_deck_list(self):
 
+        to_enable = []
         for deck_id, deck in deck_store.list_enabled():
             if deck.schedule is None:
                 continue
-            self.schedule(RunDeck(self.director, deck_id, deck.schedule))
+            to_enable.append((deck_id, deck.schedule))
+
+        # If we are not initialized we should not enable anything
+        if not config.is_initialized():
+            to_enable = []
+
+        for scheduled_task in self._scheduled_tasks[:]:
+            if not isinstance(scheduled_task, RunDeck):
+                continue
+
+            info = (scheduled_task.deck_id, scheduled_task.schedule)
+            if info in to_enable:
+                # If the task is already scheduled there is no need to
+                # enable it.
+                to_enable.remove(info)
+            else:
+                # If one of the tasks that is scheduled is no longer in the
+                # scheduled tasks. We should disable it.
+                self.unschedule(scheduled_task)
+
+        for deck_id, schedule in to_enable:
+            self.schedule(RunDeck(self.director, deck_id, schedule))
 
     def _task_did_not_run(self, failure, task):
         failure.trap(DidNotRun)
diff --git a/ooni/tests/test_scheduler.py b/ooni/tests/test_scheduler.py
index cb90a30..1350dde 100644
--- a/ooni/tests/test_scheduler.py
+++ b/ooni/tests/test_scheduler.py
@@ -1,11 +1,12 @@
 import os
 import shutil
+import random
 import tempfile
 
 from twisted.internet import defer
 from twisted.trial import unittest
 
-from ooni.agent.scheduler import ScheduledTask, DidNotRun
+from ooni.agent.scheduler import ScheduledTask, DidNotRun, FileSystemlockAndMutex
 
 class TestScheduler(unittest.TestCase):
     def test_scheduled_task(self):
@@ -49,3 +50,28 @@ class TestScheduler(unittest.TestCase):
 
         self.assertEqual(dummy_st.should_run, False)
         shutil.rmtree(scheduler_directory)
+
+
+    @defer.inlineCallbacks
+    def test_filesystem_lock_and_mutex(self):
+        lock_dir = tempfile.mkdtemp()
+        lock_path = os.path.join(lock_dir, 'lock')
+
+        lock = FileSystemlockAndMutex(lock_path)
+
+        lock_count = 100
+        unlock_count = 0
+        dl = []
+        for i in range(lock_count):
+            dl.append(lock.acquire())
+            if random.choice([0, 1]) == 0:
+                unlock_count += 1
+                lock.release()
+
+        for i in range(lock_count - unlock_count):
+            lock.release()
+
+        yield defer.DeferredList(dl)
+        self.assertFalse(lock.locked)
+
+        shutil.rmtree(lock_dir)





More information about the tor-commits mailing list