# Carbon-Aware Storage for Image-Based Time-Series — reproduction guide

This folder contains everything needed to reproduce the study **"Carbon Footprint and
Carbon-Aware Selection of Document-Oriented, Relational, and Hybrid Object-Relational
Storage for Image-Based Time-Series Workloads in Green IoT"** (target journal:
*Environment Systems and Decisions*): the benchmark **code**, the measured **data**, the
generated **figures**, and the **paper** sources.

It benchmarks three storage architectures across eight image resolutions (360p–6K),
measuring insertion, retrieval, storage amplification, and **directly measured per-resolution
carbon** (CodeCarbon + Intel RAPL), and converts the result into a carbon-aware decision
framework. It is the reproducibility artifact accompanying the paper.

---

## Download and unpack

This Zenodo record contains two files: **`README.md`** (this guide) and **`artifact.zip`**.
Download both, then unpack everything into one folder:

```bash
unzip artifact.zip       # -> code/  data/  figures/  paper/  (+ a copy of this README)
```

All commands below are run from inside that unzipped folder.

## Repository layout

```
.
├── README.md       # this file (how to reproduce + what the code/data are)
├── code/           # benchmark harness (Python) + docker-compose + tests
├── data/           # measured CSVs + emissions.csv
├── figures/        # comparison figures, PDF                (generated)
└── paper/          # main.tex, references.bib, sn-jnl.cls   (the manuscript)
```

---

## The software (`code/`)

One object-oriented harness drives all three architectures through the same measurement
protocol. Design: **Strategy** (each engine is a `StorageEngine` subclass owning its schema
and queries) + **Template Method** (the shared insert / retrieve / point-read / driver
protocol lives once in `engine_base.py` and calls small engine-specific primitives).

| File | Role |
|---|---|
| `run.py` | Single entry point: CLI, **auto-venv** bootstrap, Docker orchestration, timing/ETA, and the **retry-then-skip** failover. |
| `engine_base.py` | `StorageEngine` abstract base — the shared measurement protocol. |
| `engine_postgres.py` | **Postgre** — PostgreSQL 15 / TimescaleDB, image inline in a `BYTEA` column (TOAST). |
| `engine_mongodb.py` | **Mongo** — MongoDB 7 native Time-Series Collection, image inline as BSON `BinData`. |
| `engine_postgres_minio.py` | **PostMin** — PostgreSQL metadata + image externalised to MinIO object storage. |
| `payloads.py` | Builds deterministic JPEG payloads (a collage) from the source image at each resolution. |
| `carbon.py` | `CarbonTracker` — wraps each *(engine × operation × resolution)* in a CodeCarbon run → `emissions.csv`. |
| `results.py` | CSV writers for every dimension (+ the skip log). |
| `config.py` | Workload profiles (360p…6K), settings, and output paths. |
| `report.py` | Aggregates `data/` into `threeway_summary.csv` and renders the figures. |
| `docker-compose.yml` | `timescaledb` + `minio` + `mongodb`; only the services a phase needs are started, in isolation, for fair energy attribution. |
| `setup_rapl.sh` | One-time `sudo` helper to make Intel RAPL energy counters readable. |
| `assets/Schwarzsee.jpg` | Source image — a freely-licensed photograph of **Durdle Door, Dorset, UK** (JJ Perks, Pexels), resized to 6144×3456. |
| `tests/` | Unit tests (config, payloads, CSV writer, measurement protocol, skip logic) — no databases required. |

Four operations are measured per *(engine, resolution)*: **insertion** throughput + storage
amplification, **bulk retrieval** (read back all 2,000 images in a time range), **latest-frame
read** (fetch the single most recent image), and **driver** round-trip overhead. One cell is
skipped by design — **MongoDB at 6K** — because time-series bucketing pushes the bucket
document past MongoDB's 16 MB BSON limit; this is retried, then recorded in `data/skipped.csv`.

(For the full CLI and all environment-variable knobs, see [`code/README.md`](code/README.md).)

---

## The data (`data/`)

All CSVs are generated by `run.py`; `<engine>` ∈ `{postgres, mongo, postgres_minio}`,
`<res>` ∈ `{360p, 480p, 720p, 1080p, 1440p, 4k, 5k, 6k}` (Mongo omits `6k`).

| File pattern | Contents |
|---|---|
| `results_<engine>_insert_runs_<res>.csv` / `_summary_<res>.csv` | Per-run and summary **insertion** metrics: rows/s, durations, on-disk sizes, and **storage amplification** (on-disk ÷ raw payload). |
| `results_<engine>_retrieve_runs_<res>.csv` / `_summary_<res>.csv` | **Bulk-retrieval** latency (ms) for materialising all 2,000 payloads in a time range. |
| `results_<engine>_point_read_runs_<res>.csv` / `_summary_<res>.csv` | **Latest-frame read** latency (ms). |
| `results_<engine>_driver_summary.csv` | Minimal client–server round-trip overhead (ms). |
| `emissions.csv` | CodeCarbon output, one row per `project_name = <engine>_<operation>_<resolution>`: `duration`, `energy_consumed` (kWh), `cpu_energy`, `ram_energy`, `emissions` (kg CO₂eq), grid intensity, host info. **Per-resolution carbon = sum of the insert + retrieve + point\_read rows × 10⁶ (kg→mg).** |
| `threeway_summary.csv` | The aggregated headline table (one row per resolution): each engine's insert rows/s, storage amp, retrieval ms, point-read ms, and total carbon (mg). Built by `report.py`. |
| `skipped.csv` | Cells skipped after repeated failures (engine, resolution, attempts, error) — here, MongoDB at 6K. |

Figures in `figures/` (PDF, vector) are: `insert_throughput`, `retrieval_latency`,
`point_read_latency`, `storage_amplification`, `carbon_per_resolution`, `carbon_breakdown`.

---

## 1. Prerequisites

- **Docker** + **Docker Compose** (runs the three databases).
- **Python 3.11+** — `run.py` creates a local `.venv` and installs `requirements.txt` on first launch (set `IOTBENCH_NO_VENV=1` to use the current interpreter).
- **TeX Live** with `latexmk` + `bibtex` (to build the paper). `sn-jnl.cls`/`sn-*.bst` are bundled in `paper/`.
- *(Optional)* **Intel RAPL** for true hardware energy; otherwise CodeCarbon falls back to TDP estimates.

---

## 2. Reproduce the data and figures

To rebuild the figures and the summary table **from the shipped measurements** (no benchmark run
needed — `data/` already holds them):

```bash
cd code && python report.py    # rebuilds ../figures/ + ../data/threeway_summary.csv
```

To **regenerate the measurements from scratch** instead (needs Docker + the full sweep, which
overwrites `data/`), run:

```bash
cd code

# one-time: make Intel RAPL energy counters readable (self-elevates with sudo).
# Skip this if you accept CodeCarbon's TDP-estimate fallback.
bash setup_rapl.sh

# full sweep (360p -> 6K, all three engines), then build the report + figures:
python run.py --report
#   -> ../data/    results_*_summary_*.csv, emissions.csv, threeway_summary.csv, skipped.csv
#   -> ../figures/ insert_throughput.pdf, retrieval_latency.pdf, point_read_latency.pdf,
#                  storage_amplification.pdf, carbon_per_resolution.pdf, carbon_breakdown.pdf
```

Useful variants:

```bash
python run.py 1080p 4k 6k             # only these resolutions
python run.py 4k --engines mongodb    # one resolution, one engine
python run.py --no-docker 4k          # services already running
python run.py --dry-run               # print the plan and exit
python report.py                      # rebuild figures + threeway_summary from existing data
```

MongoDB at 6K is **automatically skipped** (16 MB BSON bucket limit) and logged to
`data/skipped.csv`; PostgreSQL and the hybrid still run at 6K.

Run the tests (no databases needed):

```bash
cd code && python -m unittest discover -s tests
```

---

## 3. Build the paper

```bash
cd paper
latexmk -pdf -interaction=nonstopmode main.tex   # runs pdflatex + bibtex -> main.pdf
latexmk -c                                        # remove aux files (keep main.pdf)
```

Figures are read from `../figures/`; the bibliography is `references.bib`. The class and
bibliography styles (`sn-jnl.cls`, `sn-basic.bst`, …) are bundled in `paper/`, so no extra
Springer package install is needed.

---

## 4. Cover letter (optional)

```bash
cd cover_letter
latexmk -pdf cover_letter.tex
```

---

## Notes

- **Source image:** `code/assets/Schwarzsee.jpg` is a photograph of Durdle Door (Dorset, UK)
  by JJ Perks via Pexels (free-use licence), resized to 6144×3456; it is cited in the paper.
- **Configuration:** workload size and ports are env-var configurable
  (`BENCHMARK_TOTAL_ROWS`, `BENCHMARK_BATCH_SIZE`, `BENCHMARK_INSERT_RUNS`, `POSTGRES_PORT`,
  `MONGO_URI`, `MINIO_ENDPOINT`, …) — see [`code/README.md`](code/README.md).
- **Exact environment** (CPU, OS, database, and library versions) used for the reported
  numbers is documented in the paper's environment table.

---

## Citation

If you use this artifact or its data, please cite the accompanying paper:

> [Authors hidden for double-anonymous review], *Carbon Footprint and Carbon-Aware Selection of
> Document-Oriented, Relational, and Hybrid Object-Relational Storage for Image-Based
> Time-Series Workloads in Green IoT*, Environment Systems and Decisions (under review).

The source image `code/assets/Schwarzsee.jpg` is a photograph of Durdle Door, Dorset, UK,
by JJ Perks via [Pexels](https://www.pexels.com/photo/bay-with-orange-seashore-under-white-and-gray-clouds-8567869/)
(free-use licence).
```
