[server] Add a "scheduler" command to run repository scheduler
authorDenis Laxalde <denis.laxalde@logilab.fr>
Tue, 21 Feb 2017 08:56:38 +0100
changeset 12016 88ed82a25f8a
parent 12015 fe057cb231b6
child 12017 281a6219bdc9
[server] Add a "scheduler" command to run repository scheduler This commands starts the repository scheduler as a standalone process that should complement a CubicWeb web instance running as a WSGI application. Added a log message in repository's shutdown method to help testing the command (i.e. make sure the method is called after the scheduler stopped). Related to #17057223.
cubicweb/server/repository.py
cubicweb/server/serverctl.py
cubicweb/server/test/unittest_serverctl.py
requirements/test-server.txt
--- a/cubicweb/server/repository.py	Mon Mar 06 15:13:44 2017 +0100
+++ b/cubicweb/server/repository.py	Tue Feb 21 08:56:38 2017 +0100
@@ -460,6 +460,7 @@
             # then, the system source is still available
             self.hm.call_hooks('before_server_shutdown', repo=self)
         self.shutting_down = True
+        self.info('shutting down repository')
         self.system_source.shutdown()
         if not (self.config.creating or self.config.repairing
                 or self.config.quick_start):
--- a/cubicweb/server/serverctl.py	Mon Mar 06 15:13:44 2017 +0100
+++ b/cubicweb/server/serverctl.py	Tue Feb 21 08:56:38 2017 +0100
@@ -980,6 +980,45 @@
             cnx.commit()
 
 
+class RepositorySchedulerCommand(Command):
+    """Start a repository tasks scheduler.
+
+    Initialize a repository and start its tasks scheduler that would run
+    registered "looping tasks".
+
+    This is maintenance command that should be kept running along with a web
+    instance of a CubicWeb WSGI application (e.g. embeded into a Pyramid
+    application).
+
+    <instance>
+      the identifier of the instance
+    """
+    name = 'scheduler'
+    arguments = '<instance>'
+    min_args = max_args = 1
+    options = (
+        ('loglevel',
+         {'short': 'l', 'type': 'choice', 'metavar': '<log level>',
+          'default': 'info', 'choices': ('debug', 'info', 'warning', 'error')},
+         ),
+    )
+
+    def run(self, args):
+        from cubicweb.cwctl import init_cmdline_log_threshold
+        from cubicweb.server.repository import Repository
+        from cubicweb.server.utils import scheduler
+        config = ServerConfiguration.config_for(args[0])
+        # Log to stdout, since the this command runs in the foreground.
+        config.global_set_option('log-file', None)
+        init_cmdline_log_threshold(config, self['loglevel'])
+        repo = Repository(config, scheduler())
+        repo.bootstrap()
+        try:
+            repo.run_scheduler()
+        finally:
+            repo.shutdown()
+
+
 class SynchronizeSourceCommand(Command):
     """Force sources synchronization.
 
@@ -1090,6 +1129,7 @@
                  DBDumpCommand, DBRestoreCommand, DBCopyCommand, DBIndexSanityCheckCommand,
                  AddSourceCommand, CheckRepositoryCommand, RebuildFTICommand,
                  SynchronizeSourceCommand, SchemaDiffCommand,
+                 RepositorySchedulerCommand,
                  ):
     CWCTL.register(cmdclass)
 
--- a/cubicweb/server/test/unittest_serverctl.py	Mon Mar 06 15:13:44 2017 +0100
+++ b/cubicweb/server/test/unittest_serverctl.py	Tue Feb 21 08:56:38 2017 +0100
@@ -1,9 +1,15 @@
 import os.path as osp
 import shutil
 
+from mock import patch
+
 from cubicweb import ExecutionError
 from cubicweb.devtools import testlib, ApptestConfiguration
-from cubicweb.server.serverctl import DBDumpCommand, SynchronizeSourceCommand
+from cubicweb.server.serverctl import (
+    DBDumpCommand,
+    RepositorySchedulerCommand,
+    SynchronizeSourceCommand,
+)
 from cubicweb.server.serverconfig import ServerConfiguration
 
 
@@ -26,6 +32,26 @@
         DBDumpCommand(None).run([self.appid])
         shutil.rmtree(osp.join(self.config.apphome, 'backup'))
 
+    def test_scheduler(self):
+        cmd = RepositorySchedulerCommand(None)
+        with patch('sched.scheduler.run',
+                   side_effect=RuntimeError('boom')) as patched_run:
+            with self.assertRaises(RuntimeError) as exc_cm:
+                with self.assertLogs('cubicweb.repository', level='INFO') as log_cm:
+                    cmd.run([self.appid])
+        # make sure repository scheduler started
+        scheduler_start_message = (
+            'INFO:cubicweb.repository:starting repository scheduler with '
+            'tasks: update_feeds, clean_sessions, expire_dataimports'
+        )
+        self.assertIn(scheduler_start_message, log_cm.output)
+        # and that scheduler's run method got called
+        self.assertIn('boom', str(exc_cm.exception))
+        patched_run.assert_called_once_with()
+        # make sure repository's shutdown method got called
+        repo_shutdown_message = 'INFO:cubicweb.repository:shutting down repository'
+        self.assertIn(repo_shutdown_message, log_cm.output)
+
     def test_source_sync(self):
         with self.admin_access.repo_cnx() as cnx:
             cnx.create_entity('CWSource', name=u'success_feed', type=u'datafeed',
--- a/requirements/test-server.txt	Mon Mar 06 15:13:44 2017 +0100
+++ b/requirements/test-server.txt	Tue Feb 21 08:56:38 2017 +0100
@@ -1,3 +1,4 @@
+mock
 psycopg2
 ldap3 < 2
 cubicweb-basket