docs: def get_upgrade_lock(dbname, connect_str, timeout=LOCK_TIMEOUT):
      """
      Wait until you can get the lock, then yield it, and eventually release it.
  
      Inspired by: http://arr.gr/blog/2016/05/mysql-named-locks-in-python-context-managers/
  
      :param dbname: database to upgrade
      :param connect_str: connection string to the database
      :param timeout: how long to wait between tries for the lock, default 5 seconds
      """
      #
      # Open connection and try to get the lock
      #
      engine = sqlalchemy.create_engine(connect_str)
      cursor = engine.execute("SELECT GET_LOCK('upgrade_{}', {})".format(dbname, timeout))
      lock = cursor.scalar()
      cursor.close()
  
      #
      # Keep trying until you get it.
      #
      while not lock:
          logger.info('Cannot acquire {} upgrade lock. Sleeping {} seconds.'.format(dbname, timeout))
          time.sleep(timeout)
          cursor = engine.execute("SELECT GET_LOCK('upgrade_{}', {})".format(dbname, timeout))
          lock = cursor.scalar()
          cursor.close()
      logger.info('Acquired {} upgrade lock'.format(dbname))
      yield lock
  
      #
      # Release the lock and close the connection.
      #
      cursor = engine.execute("SELECT RELEASE_LOCK('upgrade_{}')".format(dbname))
      cursor.close()
      engine.dispose()
      logger.info('Released {} upgrade lock'.format(dbname))