[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.
--- 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