// Наблюдение за GC: страницы newly/cow, рекламация FIFO vs LIFO
// Раздел учебника: Том III, глава 16, «GC», §16.5 «FIFO vs LIFO».
// Что демонстрирует: цикл «вставка+удаление+коммит» и статистику страниц
//   (newly/cow через mi_pgop_stat); сравнение роста файла при FIFO и LIFO
//   рекламации (MDBX_LIFORECLAIM).
// Сборка: g++ -std=c++17 -I<libmdbx-include> -I../common 21-gc-observe.c++ -lmdbx
// Запуск: ./21-gc-observe
// Ожидаемый вывод (числа зависят от системы):
//   pgop after churn: newly=<N> cow=<M>
//   fifo size = <S>, lifo size = <S2>
//   ok: GC pages observed; LIFO reclaim toggled
// При сбое — сообщение в stderr и exit 1.
//
// SPDX-License-Identifier: Apache-2.0

#include <cstdint>
#include <iostream>
#include <string>

#include "common.h++"
#include <mdbx.h++>

static uint64_t file_size(const mdbx::env_managed &env) { return env.get_info().mi_dxb_fsize; }

static void churn(mdbx::env_managed &env) {
  for (int round = 0; round < 10; ++round) {
    auto txn = env.start_write();
    auto table = txn.open_map(nullptr);
    for (int i = 0; i < 500; ++i) {
      const auto key = "k" + std::to_string(round * 1000 + i);
      txn.insert(table, mdbx::slice(key), mdbx::slice("payload"));
    }
    txn.commit();
    auto del = env.start_write();
    auto dtable = del.open_map(nullptr);
    for (int i = 0; i < 500; ++i) {
      const auto key = "k" + std::to_string(round * 1000 + i);
      del.erase(dtable, mdbx::slice(key));
    }
    del.commit();
  }
}

static uint64_t run_churn(const std::string &path, mdbx::env::operate_parameters params) {
  mdbx::env_managed env(path, mdbx::env_managed::create_parameters(), params);
  churn(env);
  return file_size(env);
}

int main() {
  try {
    const std::string path = std::string(example::tmpdir()) + "/mdbx-21-gc-observe.mdbx";
    mdbx::env::remove(path);

    // FIFO (по умолчанию): страницы GC переиспользуются в порядке освобождения.
    {
      mdbx::env_managed env = example::env_open(path);
      churn(env);
      const auto info = env.get_info();
      std::cout << "pgop after churn: newly=" << info.mi_pgop_stat.newly << " cow=" << info.mi_pgop_stat.cow << "\n";
    }
    const uint64_t fifo_size = run_churn(path, mdbx::env::operate_parameters());

    // LIFO: свежеосвобождённые страницы используются в первую очередь.
    mdbx::env::remove(path);
    mdbx::env::operate_parameters params;
    // MDBX_LIFORECLAIM
    params.set_reclaiming(mdbx::env::reclaiming_options().set_lifo(true));
    const uint64_t lifo_size = run_churn(path, params);
    std::cout << "fifo size = " << fifo_size << ", lifo size = " << lifo_size << "\n";

    std::cout << "ok: GC pages observed; LIFO reclaim toggled\n";
    return EXIT_SUCCESS;
  } catch (const std::exception &ex) {
    std::cerr << "error: " << ex.what() << "\n";
    return EXIT_FAILURE;
  }
}