Commit ca82063b authored by Ramon Nou's avatar Ramon Nou
Browse files

Merge branch 'rnou/opt' into 'master'

Fixes and optimizations

This MR solves:
* Malleability issues due a put instead a update on rocksdb on metadata movements (old stale metadata status could survive)
* Malleability tool : gkfs_malleabiliy mutate sync command : Synchronously traverses all the malleability phases
* Add a weight + benchmark phase before the daemons are ready so the servers are weighted per write bandwidth. It can even decide to go to memory (or a secondary root) if we hit a threshold
* Needs Random Slicing and GKFS_RANDOM_SLICING_CALIBRATION and ( GKFS_DAEMON_2ND_ROOT with GKFS_RANDOM_SLICING_MIN_MBPS , optional )

See merge request !320
parents bd078e2c ee2df534
Loading
Loading
Loading
Loading
Loading
+7 −1
Changes for CHANGELOG.md: 7 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
    - Enable inline data for small files (`LIBGKFS_USE_INLINE_DATA`).
    - Create write optimization (`LIBGKFS_CREATE_WRITE_OPTIMIZATION`).
    - Read inline prefetch (`LIBGKFS_READ_INLINE_PREFETCH`).
    - Dirents compression (`LIBGKFS_USE_DIRENTS_COMPRESSION and GKFS_DAEMON_USE_DIRENTS_COMPRESSION`). 
    - Dirents compression (`GKFS_USE_DIRENTS_COMPRESSION`).
    - Dirents buffer size control (`LIBGKFS_DIRENTS_BUFF_SIZE`).
    - New sfind filtering in the server side
  - Added new tests (and enabling failing ones) to increase coverage
@@ -69,6 +69,12 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
    - Added complete 2026 source headers, documentation for important APIs, and safe Cppcheck fixes.
    - Added replica-aware placement, deterministic read fallback, degraded-operation policies, host health, repair queues, restart-safe repair journals, and replica metrics.
    - Added copy-indexed migration plans with generation validation, source-copy reads, Simple Hash and Random Slicing topology mutation support, checkpoint recovery, and daemon-loss/restart integration coverage.
  - Malleability and distribution improvements ([!320](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/320))
    - Added blocking `mutate sync` and explicit `expand`/`shrink` commands, with hostfile validation, status polling, and finalize after successful redistribution.
    - Added optional storage-calibrated Random Slicing weights, calibration statistics in the hostfile, and startup readiness checks.
    - Corrected old/new host ordering and ownership during mutation, including shrink metadata migration; retried legacy directory RPCs after `ENOBUFS`.
    - Added `GKFS_DAEMON_MIGRATION_PARALLELISM` (default `1`, range `1`–`32`) for bounded parallel eager chunk migration per daemon, with completion-safe progress and checkpoint accounting.
    - Added mutation integration coverage for serial, parallel, and invalid worker settings, including data integrity, checkpoints, and daemon selection logs.


### Changed 
+67 −5
Changes for README.md: 67 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -514,7 +514,31 @@ GekkoFS supports changing the daemon topology while preserving existing data and

During redistribution, applications must not access the mounted GekkoFS file system. If the GekkoFS proxy is used, proxies must be restarted manually after the topology change.

To build the control tool, configure with `-DGKFS_BUILD_TOOLS=ON`. This builds `gkfs_malleability`. The control tool reads the marker hostfile directly; it does not need separate expand or shrink hostfiles and it does not start daemons. The `scripts/run/gkfs` wrapper uses the same model: prepare one workspace hostfile with `+` and `-` markers, make sure added daemons are already running, then run `scripts/run/gkfs mutate` to call `gkfs_malleability mutate start/status/finalize` and let finalize clean the workspace hostfile.
To build the control tool, configure with `-DGKFS_BUILD_TOOLS=ON`. This builds `gkfs_malleability`. It supports three workflows:

1. **Marker-based asynchronous workflow:** use `mutate start`, `mutate status`, and `mutate finalize`. Mark removals with `-`; for additions, start the new daemons with `GKFS_DAEMON_EXPAND=ON` so they publish `+` entries. The caller controls when to poll and finalize.
2. **Marker-based synchronous workflow:** prepare the same `+`/`-` markers, then run `mutate sync`. It performs the start, waits for redistribution, and finalizes before returning.
3. **Explicit synchronous workflow:** use `expand <host...>` / `shrink <host...>`. Start all current and target daemons normally and ensure their entries are in the same hostfile. Do not add `+` or `-` markers manually and do not set `GKFS_DAEMON_EXPAND`; the command writes the needed marker internally, runs redistribution, and finalizes synchronously.

For all workflows, set `GKFS_DAEMON_KEEP_HOSTS_FILE=ON` on daemons that may stop, so shutdown preserves the shared hostfile through finalization.

The `scripts/run/gkfs mutate` wrapper uses the asynchronous marker workflow. It does not start daemons.

Examples of the explicit synchronous commands:

```bash
gkfs_malleability expand node3 node4
gkfs_malleability shrink node1 node2
```

All daemons, including expansion targets, must already be running and
registered in the shared hostfile, and `GKFS_DAEMON_KEEP_HOSTS_FILE=ON` must
preserve that file during daemon shutdown. Host-list commands do not require
`GKFS_DAEMON_EXPAND=ON` or manually authored markers. Hostnames without
rootdir suffixes work only when they identify one entry unambiguously; an RPC
URI can identify a daemon on a shared host. Each command runs redistribution
and finalization synchronously. If redistribution fails, the markers remain
for diagnosis or retry. A shrink must leave at least one daemon active.

The clients can be restarted, or the user can sent to them a signal (SIGUSR1) to force a reload of the hostfile.

@@ -525,7 +549,7 @@ Malleability uses one workspace hostfile. Entries in this file are the source of
| Line form | Meaning |
|---|---|
| `node uri ...` | active daemon |
| `+node uri ...` | daemon being added; new daemons write this automatically when started with `GKFS_DAEMON_EXPAND=ON` |
| `+node uri ...` | daemon being added; the synchronous `expand` command writes this marker |
| `-node uri ...` | daemon being removed |
| `# GKFS_RS_INTERVAL host=<id> start=<x> end=<y>` | Random Slicing interval-table comment; ignored by normal hostfile parsing but loaded by Random Slicing clients/daemons |
| `# GKFS_RS_LAYOUT version=1 hash=<hash>` | Versioned canonical hash for the adjacent Random Slicing interval table; a present record must match before clients/daemons install the table |
@@ -627,6 +651,27 @@ wait_for_mutate() {

#### Expand: add daemon nodes

To allow concurrent eager chunk transfers, set the daemon environment **before
launching both existing and new daemons** (not just on the `gkfs_malleability`
CLI):

```bash
export GKFS_DAEMON_MIGRATION_PARALLELISM=4
```

This permits up to four simultaneous chunk migration jobs **per daemon** during
`mutate start`. The default is `1` (serial); supported values are `1` through
`32`. It applies to primary and replicated chunk transfers, not metadata
redistribution or `GKFS_EXPAND_ON_DEMAND=ON` lazy reads. More workers may use
more memory and overload receiving daemons; compare mutation time and
destination load before raising the value.

The integration test in
`tests/integration/malleability/test_migration_parallelism.py` checks `1`,
`4`, and an invalid value during a real expand. It verifies file contents,
migrated chunks, daemon selection/fallback logs, and migration checkpoints.
The `[parallel]` unit tests cover actual concurrent execution and accounting.

Start the initial instance, for example on two nodes:

```bash
@@ -758,6 +803,7 @@ Useful daemon and tool options for these examples:
| `gkfs_malleability mutate start` | Start redistribution from the marked hostfile topology |
| `gkfs_malleability mutate status` | Poll redistribution status |
| `gkfs_malleability mutate finalize` | Stop daemons marked with `-`, clean the hostfile markers, and complete the topology change |
| `gkfs_malleability mutate sync` | Start redistribution, wait for successful completion, and finalize in one blocking command |

### Random Slicing and CutShift

@@ -785,6 +831,19 @@ Reference: Random Slicing is based on "Random slicing: Efficient and scalable da

Use this when you manage daemon startup and hostfile markers yourself.

For a blocking workflow, after preparing the marker hostfile and starting any
added daemons, run:

```bash
export LIBGKFS_HOSTS_FILE=/path/to/gkfs_workspace.txt
gkfs_malleability mutate sync
```

The command returns `0` after successful redistribution and finalize, and a
non-zero exit code if start, status polling, redistribution, or finalize fails.
It leaves the marker hostfile unchanged on redistribution failure so the
operation can be diagnosed or retried.

Expand manually:

```bash
@@ -969,7 +1028,7 @@ Environment variable prefixes are scoped by component:
- `GKFS_USE_DIRENTS_COMPRESSION` - Enable compression for directory entries (default: OFF).
- `GKFS_HOSTS_FILE` - Legacy daemon-side hostfile path.

### Daemon metrics (startup-only)
### Daemon metrics and migration settings
- `GKFS_DAEMON_ENABLE_STATS` - Enable general daemon statistics collection (default: OFF).
- `GKFS_DAEMON_ENABLE_CHUNKSTATS` - Enable chunk statistics collection (default: OFF).
- `GKFS_DAEMON_ENABLE_PROMETHEUS` - Enable Prometheus output and general operation-stat collection when compiled with `GKFS_ENABLE_PROMETHEUS` (default: OFF).
@@ -978,9 +1037,12 @@ Environment variable prefixes are scoped by component:
- `GKFS_DAEMON_STATS_FILE` - Write periodic statistics to this file.
- `GKFS_DAEMON_STATS_LOG` - Write periodic statistics to the configured daemon log file (default: OFF).
- `GKFS_DAEMON_MIGRATION_CHECKPOINT` - Enable atomic per-daemon migration checkpoints (default: OFF).
- `GKFS_DAEMON_MIGRATION_PARALLELISM` - Maximum concurrent chunk migration jobs per daemon during eager mutation (integer 1–32; default: 1). Applies to primary and replicated chunk transfers, not metadata redistribution. Each worker can hold a chunk buffer; increase carefully to avoid overloading destinations. Invalid values fall back to 1.

These variables are read once during daemon startup. They add no statistics
collection overhead when unset. Existing command-line options remain supported.
Set daemon environment variables before launching daemons. Metrics settings
are read during startup; migration parallelism is read when eager chunk
redistribution starts. Unset metrics settings add no statistics collection
overhead. Existing command-line options remain supported.

Periodic snapshots include a wall-clock `timestamp_ms`, daemon `pid`,
`uptime_seconds`, sampled Argobots I/O queue depth (`io_queue_depth`) and total
+4 −0
Changes for docs/sphinx/users/running.md: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -320,6 +320,10 @@ Set `GKFS_DAEMON_STATS_FILE=/path/to/stats.log` for periodic file output, or
When all these variables are unset, no statistics collection thread is started.
Set `GKFS_DAEMON_MIGRATION_CHECKPOINT=ON` to enable atomic per-daemon migration
checkpoint files; this is startup-only and disabled by default.
Set `GKFS_DAEMON_MIGRATION_PARALLELISM=4` on the daemons to run up to four
concurrent eager chunk migration jobs per daemon (range 1–32, default 1).
This does not parallelize metadata redistribution. Each worker can hold a
chunk buffer, so tune against destination load and available memory.

Snapshots include `timestamp_ms`, `pid`, `uptime_seconds`, sampled I/O queue
depth/total, migration job/byte counters, daemon operation totals, and read/write
+10 −0
Changes for include/common/env.hpp: 10 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -58,6 +58,16 @@ static constexpr auto DISTRIBUTION_STRATEGY =
        ADD_PREFIX("DISTRIBUTION_STRATEGY");
static constexpr auto RANDOM_SLICING_CUTSHIFT =
        ADD_PREFIX("RANDOM_SLICING_CUTSHIFT");
static constexpr auto RANDOM_SLICING_CALIBRATION =
        ADD_PREFIX("RANDOM_SLICING_CALIBRATION");
static constexpr auto RANDOM_SLICING_CALIBRATION_SECONDS =
        ADD_PREFIX("RANDOM_SLICING_CALIBRATION_SECONDS");
static constexpr auto RANDOM_SLICING_CALIBRATION_MIN_MBPS =
        ADD_PREFIX("RANDOM_SLICING_CALIBRATION_MIN_MBPS");
static constexpr auto RANDOM_SLICING_CALIBRATION_DAEMON_COUNT =
        ADD_PREFIX("RANDOM_SLICING_CALIBRATION_DAEMON_COUNT");
static constexpr auto RANDOM_SLICING_CALIBRATION_DAEMON_ID =
        ADD_PREFIX("RANDOM_SLICING_CALIBRATION_DAEMON_ID");
static constexpr auto EXPAND_ON_DEMAND = ADD_PREFIX("EXPAND_ON_DEMAND");
static constexpr auto NUM_REPL = ADD_PREFIX("NUM_REPL");
static constexpr auto CREATE_CHECK_PARENTS = ADD_PREFIX("CREATE_CHECK_PARENTS");
+4 −0
Changes for include/common/hostfile_management.hpp: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -135,6 +135,10 @@ get_marker(const std::string& line);
HostfileMarkers
parse_hostfile_markers(const std::string& path);

/** Return per-host RS weights in the same sorted order as hostfile_pairs(). */
std::vector<float>
hostfile_rs_weights(const std::string& path);

/**
 * @brief Validate a marker-based hostfile topology.
 * @param markers Parsed hostfile markers
Loading