text: # pylint: disable=unused-argument
  # pylint: disable=redefined-outer-name
  # pylint: disable=line-too-long
  
  # This test verifies that memory tracking does not have significant drift,
  # in other words, every allocation should be taken into account at the global
  # memory tracker.
  #
  # So we are running some queries with GROUP BY to make some allocations,
  # and after we are checking MemoryTracking metric from system.metrics,
  # and check that it does not changes too frequently.
  #
  # Also note, that syncing MemoryTracking with RSS had been disabled in
  # asynchronous_metrics_update_period_s.xml.
  
  import logging
  import pytest
  from helpers.cluster import ClickHouseCluster
  
  cluster = ClickHouseCluster(__file__)
  
  node = cluster.add_instance('node', main_configs=[
      'configs/no_system_log.xml',
      'configs/asynchronous_metrics_update_period_s.xml',
  ], user_configs=[
      'configs/users.d/overrides.xml',
  ])
  
  @pytest.fixture(scope='module', autouse=True)
  def start_cluster():
      try:
          cluster.start()
          yield cluster
      finally:
          cluster.shutdown()
  
  query_settings = {
      'max_threads': 1,
      'log_queries': 0,
  }
  sample_query = "SELECT groupArray(repeat('a', 1000)) FROM numbers(10000) GROUP BY number%10 FORMAT JSON"
  
  def query(*args, **kwargs):
      if 'settings' not in kwargs:
          kwargs['settings'] = query_settings
      else:
          kwargs['settings'].update(query_settings)
      return node.query(*args, **kwargs)
  def http_query(*args, **kwargs):
      if 'params' not in kwargs:
          kwargs['params'] = query_settings
      else:
          kwargs['params'].update(query_settings)
      return node.http_query(*args, **kwargs)
  
  def get_MemoryTracking():
      return int(http_query("SELECT value FROM system.metrics WHERE metric = 'MemoryTracking'"))
  
  def check_memory(memory):
      # bytes -> megabytes
      memory = [*map(lambda x: int(int(x)/1024/1024), memory)]
      # 3 changes to MemoryTracking is minimum, since:
      # - this is not that high to not detect inacuracy
      # - memory can go like X/X+N due to some background allocations
      # - memory can go like X/X+N/X, so at least 2 changes
      changes_allowed = 3
      # if number of samples is large enough, use 10% from them
      # (actually most of the time there will be only few changes, it was made 10% to avoid flackiness)
      changes_allowed_auto=int(len(memory) * 0.1)
      changes_allowed = max(changes_allowed_auto, changes_allowed)
  
      changed=len(set(memory))
      logging.info('Changes: allowed=%s, actual=%s, sample=%s',
          changes_allowed, changed, len(memory))
      assert changed < changes_allowed
  
  def test_http():
      memory = []
      memory.append(get_MemoryTracking())
      for _ in range(100):
          http_query(sample_query)
          memory.append(get_MemoryTracking())
      check_memory(memory)
  
  def test_tcp_multiple_sessions():
      memory = []
      memory.append(get_MemoryTracking())
      for _ in range(100):
          query(sample_query)
          memory.append(get_MemoryTracking())
      check_memory(memory)
  
  def test_tcp_single_session():
      memory = []
      memory.append(get_MemoryTracking())
      sample_queries = [
          sample_query,
          "SELECT metric, value FROM system.metrics WHERE metric = 'MemoryTracking'",
      ] * 100
      rows = query(';'.join(sample_queries))
      memory = rows.split('\n')
      memory = filter(lambda x: x.startswith('MemoryTracking'), memory)
      memory = map(lambda x: x.split('\t')[1], memory)
      memory = [*memory]
      check_memory(memory)
  