<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【odoo】【知識雜談】單一實例多庫模式下定時任務的問題分析

      歡迎轉載,但需標注出處,謝謝!

      背景:

      有客戶反應有個別模塊下的定時任務沒有正常執行,是否是新裝的模塊哪些有問題?排查后發現,客戶是在一臺服務器上跑著一個odoo容器,對應多個數據庫。個別庫的定時任務是正常的,但是一個對接其他平臺的庫的定時任務沒有正常跑起來。

      先說結論,看官沒時間支持按說明處理即可,分析過程在下面。

      結論

      在odoo的配置文件db_name字段配置希望后臺一直跑著的庫名稱字符串,以英文“,”分割。

      分析

      直接源碼

      1. 看odoo日志,我們知道odoo的任務正常執行時會打印Starting Job 任務名稱,直接vscode全局查找,定位到ir_cron.py文件的_process_jobs函數。
          @classmethod
          def _process_jobs(cls, db_name):
              """ Try to process all cron jobs.
      
              This selects in database all the jobs that should be processed. It then
              tries to lock each of them and, if it succeeds, run the cron job (if it
              doesn't succeed, it means the job was already locked to be taken care
              of by another thread) and return.
      
              :raise BadVersion: if the version is different from the worker's
              :raise BadModuleState: if modules are to install/upgrade/remove
              """
              db = odoo.sql_db.db_connect(db_name)
              threading.current_thread().dbname = db_name
              try:
                  with db.cursor() as cr:
                      # Make sure the database has the same version as the code of
                      # base and that no module must be installed/upgraded/removed
                      cr.execute("SELECT latest_version FROM ir_module_module WHERE name=%s", ['base'])
                      (version,) = cr.fetchone()
                      cr.execute("SELECT COUNT(*) FROM ir_module_module WHERE state LIKE %s", ['to %'])
                      (changes,) = cr.fetchone()
                      if version is None:
                          raise BadModuleState()
                      elif version != BASE_VERSION:
                          raise BadVersion()
                      # Careful to compare timestamps with 'UTC' - everything is UTC as of v6.1.
                      cr.execute("""SELECT * FROM ir_cron
                                    WHERE numbercall != 0
                                        AND active AND nextcall <= (now() at time zone 'UTC')
                                    ORDER BY priority""")
                      jobs = cr.dictfetchall()
      
                  if changes:
                      if not jobs:
                          raise BadModuleState()
                      # nextcall is never updated if the cron is not executed,
                      # it is used as a sentinel value to check whether cron jobs
                      # have been locked for a long time (stuck)
                      parse = fields.Datetime.from_string
                      oldest = min([parse(job['nextcall']) for job in jobs])
                      if datetime.now() - oldest > MAX_FAIL_TIME:
                          odoo.modules.reset_modules_state(db_name)
                      else:
                          raise BadModuleState()
      
                  for job in jobs:
                      lock_cr = db.cursor()
                      try:
                          # Try to grab an exclusive lock on the job row from within the task transaction
                          # Restrict to the same conditions as for the search since the job may have already
                          # been run by an other thread when cron is running in multi thread
                          lock_cr.execute("""SELECT *
                                             FROM ir_cron
                                             WHERE numbercall != 0
                                                AND active
                                                AND nextcall <= (now() at time zone 'UTC')
                                                AND id=%s
                                             FOR UPDATE NOWAIT""",
                                         (job['id'],), log_exceptions=False)
      
                          locked_job = lock_cr.fetchone()
                          if not locked_job:
                              _logger.debug("Job `%s` already executed by another process/thread. skipping it", job['cron_name'])
                              continue
                          # Got the lock on the job row, run its code
                          _logger.info('Starting job `%s`.', job['cron_name'])
                          job_cr = db.cursor()
                          try:
                              registry = odoo.registry(db_name)
                              registry[cls._name]._process_job(job_cr, job, lock_cr)
                              _logger.info('Job `%s` done.', job['cron_name'])
                          except Exception:
                              _logger.exception('Unexpected exception while processing cron job %r', job)
                          finally:
                              job_cr.close()
      
                      except psycopg2.OperationalError as e:
                          if e.pgcode == '55P03':
                              # Class 55: Object not in prerequisite state; 55P03: lock_not_available
                              _logger.debug('Another process/thread is already busy executing job `%s`, skipping it.', job['cron_name'])
                              continue
                          else:
                              # Unexpected OperationalError
                              raise
                      finally:
                          # we're exiting due to an exception while acquiring the lock
                          lock_cr.close()
      
              finally:
                  if hasattr(threading.current_thread(), 'dbname'):
                      del threading.current_thread().dbname
      
      1. 看到上面這個函數已經是執行的具體內容了。我們繼續在該文件查找_process_jobs函數 => _acquire_job函數 => server.py文件中的cron_thread函數 => cron_spawn函數。
        其中cron_spawn定了開啟幾個cron線程,由max_cron_threads決定。
        在cron_thread函數中,我們可以看到定時任務的調用過程
          def cron_thread(self, number):
              from odoo.addons.base.models.ir_cron import ir_cron
              while True:
                  time.sleep(SLEEP_INTERVAL + number)     # Steve Reich timing style
                  registries = odoo.modules.registry.Registry.registries
                  _logger.debug('cron%d polling for jobs', number)
                  for db_name, registry in registries.d.items():
                      if registry.ready:
                          thread = threading.currentThread()
                          thread.start_time = time.time()
                          try:
                              ir_cron._acquire_job(db_name)
                          except Exception:
                              _logger.warning('cron%d encountered an Exception:', number, exc_info=True)
                          thread.start_time = None
      
      1. 核心內容是registries.d.items(),cron線程將循環調用registries中的數據庫信息,那么這個變量中到底有哪些內容,如何添加的呢?可以全局搜索registries,定位到Registry.py文件(具體的registry類對象)以及server.py中的preload_registries函數以及調用該函數的run函數??疵Q可以了解將預加載registry信息。
      
          def run(self, preload=None, stop=False):
              """ Start the http server and the cron thread then wait for a signal.
      
              The first SIGINT or SIGTERM signal will initiate a graceful shutdown while
              a second one if any will force an immediate exit.
              """
              self.start(stop=stop)
      
              rc = preload_registries(preload)
      
              if stop:
                  if config['test_enable']:
                      logger = odoo.tests.runner._logger
                      with Registry.registries._lock:
                          for db, registry in Registry.registries.d.items():
                              report = registry._assertion_report
                              log = logger.error if not report.wasSuccessful() \
                               else logger.warning if not report.testsRun \
                               else logger.info
                              log("%s when loading database %r", report, db)
                  self.stop()
                  return rc
      
      1. 核心是run函數匯總的preload變量,記錄著將初始化哪些數據庫的對象;

      2. 查找上級為,server.py中的main函數,至此所有思路都清晰了,看源碼

      
      def main(args):
          check_root_user()
          odoo.tools.config.parse_config(args)
          check_postgres_user()
          report_configuration()
      
          config = odoo.tools.config
      
          # the default limit for CSV fields in the module is 128KiB, which is not
          # quite sufficient to import images to store in attachment. 500MiB is a
          # bit overkill, but better safe than sorry I guess
          csv.field_size_limit(500 * 1024 * 1024)
      
          preload = []
          if config['db_name']:
              preload = config['db_name'].split(',')
              for db_name in preload:
                  try:
                      odoo.service.db._create_empty_database(db_name)
                      config['init']['base'] = True
                  except ProgrammingError as err:
                      if err.pgcode == errorcodes.INSUFFICIENT_PRIVILEGE:
                          # We use an INFO loglevel on purpose in order to avoid
                          # reporting unnecessary warnings on build environment
                          # using restricted database access.
                          _logger.info("Could not determine if database %s exists, "
                                       "skipping auto-creation: %s", db_name, err)
                      else:
                          raise err
                  except odoo.service.db.DatabaseExists:
                      pass
      
          if config["translate_out"]:
              export_translation()
              sys.exit(0)
      
          if config["translate_in"]:
              import_translation()
              sys.exit(0)
      
          # This needs to be done now to ensure the use of the multiprocessing
          # signaling mecanism for registries loaded with -d
          if config['workers']:
              odoo.multi_process = True
      
          stop = config["stop_after_init"]
      
          setup_pid_file()
          rc = odoo.service.server.start(preload=preload, stop=stop)
          sys.exit(rc)
      

      在上面,我們preload為配置文件中的db_name的值,那么正向梳理回去就是
      a) odoo在啟動的時候加載odoo.conf配置文件,并讀取db_name的值
      b) 加載完成后將通過db_name的值初始化數據庫對象;
      c) 并在完成cron線程初始化后循環調用庫對象,執行相關定時任務。

      posted @ 2021-09-06 08:49  老韓頭的開發日常  閱讀(941)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久久天天躁狠狠躁夜夜2020老熟妇 | 午夜射精日本三级| 亚洲中文字幕综合小综合| 亚洲av成人午夜福利| 日韩秘 无码一区二区三区| 欧美人妻在线一区二区| 色哟哟www网站入口成人学校| 毛片无码一区二区三区| 日韩精品一区二区三免费| 色哟哟www网站入口成人学校| 波多野结衣久久一区二区| 鲜嫩高中生无套进入| 精品国产伦理国产无遮挡| 阳原县| 精品人妻日韩中文字幕| 欧美人成在线播放网站免费| 国产精品爽爽va在线观看网站 | 亚洲欧美综合中文| 久久精品人妻无码一区二区三区| 久久国产精品久久精品国产| 亚洲一区二区美女av| 兴城市| 一区二区不卡国产精品| 国产女人在线视频| 窝窝午夜色视频国产精品破| 人妻中文字幕一区二区三 | 国产办公室秘书无码精品99| 亚洲丰满熟女一区二区蜜桃| 性色av免费观看| 午夜福利看片在线观看| 亚洲熟妇自偷自拍另欧美| 国产精品青青在线观看爽香蕉| 久久免费观看午夜成人网站 | 国产三级国产精品国产专| 办公室强奷漂亮少妇视频| 无码专区 人妻系列 在线| 精品人妻中文字幕av| 亚洲伊人精品久视频国产| 中文字幕亚洲无线码A| 国产精品亚洲中文字幕| 国产第一页屁屁影院|