# HG changeset patch # User Denis Laxalde # Date 1487663798 -3600 # Node ID 88ed82a25f8aea23e99326a3a9611ec0e7a09e76 # Parent fe057cb231b6c367fe307923e95de8da1c197611 [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. diff -r fe057cb231b6 -r 88ed82a25f8a cubicweb/server/repository.py --- 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): diff -r fe057cb231b6 -r 88ed82a25f8a cubicweb/server/serverctl.py --- 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). + + + the identifier of the instance + """ + name = 'scheduler' + arguments = '' + min_args = max_args = 1 + options = ( + ('loglevel', + {'short': 'l', 'type': 'choice', 'metavar': '', + '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) diff -r fe057cb231b6 -r 88ed82a25f8a cubicweb/server/test/unittest_serverctl.py --- 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', diff -r fe057cb231b6 -r 88ed82a25f8a requirements/test-server.txt --- 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