This directory contains scripts that take a Battle School Workshop lab from
its default "Lab 0" baseline to a state where trim_excess_metadata.sh can be
run and end-to-end tested against a realistic post-divestiture Perforce server.
These scripts automate the manual setup that would otherwise take 20–30 minutes of careful interactive work. They are intended for:
This test harness requires the Battle School Workshop lab.
Battle School is a Perforce-internal training and dev/test environment that provides pre-configured, safe-to-play, non-production lab instances running the full SDP (Server Deployment Package) stack. It is an excellent platform for developing and validating admin scripts that require sophisticated, realistic multi-server topologies — without any risk to production data.
| Host | Role | ServerID | Port |
|---|---|---|---|
| p4c-bos-01 | Commit (master) | commit.p4demo.1 | 1999 |
| p4c-bos-02 | HA standby | p4d_ha_bos | 1999 |
| p4c-nyc-03 | DR standby (sacrificed) | p4d_fs_nyc | 1999 |
| p4c-syd-04 | Edge | p4d_edge_syd | 1999 |
| p4c-syd-05 | Edge HA | p4d_ha_edge_syd | 1999 |
All servers are SDP-managed, SSH-accessible as the perforce OS user without a
password (from bos-01 and among themselves).
The lab ships with sample depot data in //depot/... (Jam, OpenOffice source,
etc.) and a set of standard users, groups, clients, and jobs.
Why nyc-03? It is the DR standby — the most expendable server in the topology. We transform it into a simulated "divested unit" that was originally a filtered forwarding replica promoted to a standalone commit server.
The scenario is a filtered-replication divestiture:
//jam/... and //pb/...) out of many.trim_excess_metadata.sh needs to clean up the leftover metadata on that
server: users, clients, groups, labels, branches, jobs, server specs, remote
specs, typemap, triggers, protections, depot specs, and finally snap the
lazy-copy archive links and delete orphaned archive directories.Perforce uses lazy copy for branching — //jam/... files branched from
//depot/Jam/... share physical archive files. When the replica's
RevisionDataFilter keeps //jam/... but filters out //depot/..., p4verify
still pulls the //depot/... archive files because //jam/... needs them.
After promotion to standalone, //depot/... has 0 db.rev records (looks empty)
but p4 depot -df refuses to delete it because db.storage still has entries.
The correct resolution is:
p4 snap //jam/... //pb/... — breaks lazy-copy links (gives kept depots
their own physical archive copies)p4d -jr) — removes the depot spec from db.domainrm -rf the orphaned archive directories (Phase 18 output)| Script | Runs on | Purpose |
|---|---|---|
setup_lab.sh |
p4c-bos-01 | Full Lab 0 → trim-ready setup (orchestrates all steps, SSHes to nyc-03 as needed) |
run_trim_test.sh |
p4c-bos-01 | Run trim script: dry run → live → snap; show journal patch apply command |
From p4c-bos-01 as the perforce OS user:
# 1. Reset lab to Lab 0 baseline (via Battle School UI or reset tool)
# 2. Run setup (takes ~5 minutes; p4verify pulling archives is the slow part)
cd /home/perforce/tem/test
bash setup_lab.sh
# 3. Run the trim test
bash run_trim_test.sh
After run_trim_test.sh completes, it prints the journal patch path and the
exact p4d -jr command to apply it. The patch file must be copied to nyc-03
before applying:
JNL=/home/perforce/tem/trim_excess_metadata.<timestamp>.jnl
scp "$JNL" perforce@p4c-nyc-03:/tmp/
ssh perforce@p4c-nyc-03 "sudo systemctl stop p4d_1 && \
p4d -r /p4/1/root -jr /tmp/$(basename $JNL) && \
sudo systemctl start p4d_1"
Then verify and clean up orphaned archives (rm -rf commands are in Phase 18 output and the trim script summary):
export P4CONFIG=/home/perforce/tem/.p4config.gf
p4 verify //jam/... //pb/...
ssh perforce@p4c-nyc-03 'rm -rf /p4/1/depots/depot'
After a successful run_trim_test.sh:
| Check | Expected |
|---|---|
p4 users |
perforce only |
p4 groups |
testers only |
p4 depots |
jam, pb, Perforce (remote), spec, unload |
p4 clients |
perforce-owned clients only (or none) |
p4 servers |
p4d_commit_gf only |
p4 remotes |
none |
p4 typemap |
empty |
p4 triggers |
as per default_triggers.p4s |
p4 verify //jam/... //pb/... |
no errors |
| Journal patch exit code | 0 |
| Trim script exit code (after snap) | 4 (4 shelved CL failures — expected, in filtered depots) |
The trim script uses two keep files, created by setup_lab.sh:
| File | Contents | Purpose |
|---|---|---|
keep_users.gf.txt |
perforce |
Only the perforce admin survives the trim |
keep_groups.gf.txt |
testers |
Tests the last-group-member retry logic (Randall_Scott) |
The testers group has Randall_Scott as its only member. Deleting
Randall_Scott would fail with "last member of group testers" — a specific
error the trim script handles by temporarily adding a placeholder owner, then
deleting the user, then cleaning up.
mkrep.sh creates svc_p4d_ffr_gf, the
configurable dm.user.resetpassword=1 marks the account as requiring a
password reset. setup_lab.sh sets a known lab password and logs in.p4d vs /p4/1/bin/p4d_1: Always use the p4d SDP wrapper; it calls
the correct instance binary based on $SDP_INSTANCE.sudo systemctl start|stop|restart|status p4d_1
— not manual p4d invocations. systemctl start returns immediately; the
service takes ~2–3 seconds to come online.run.users.authorize=1: Do NOT remove this security configurable. The
setup_lab.sh ensures the perforce user is logged in before running trim.p4d_commit_gf) rather
than reusing p4d_ffr_gf. Reusing the old ID would inherit scoped
configurables like db.replication=readonly.auth.id must be unset so the standalone
handles its own authentication.p4d -jr FILE reads FILE from the local
filesystem of the machine running p4d. If the trim script runs on bos-01
and p4d is on nyc-03, the .jnl file must be SCP'd to nyc-03 first.# Test Environment for trim_excess_metadata.sh
## Overview
This directory contains scripts that take a **Battle School Workshop** lab from
its default "Lab 0" baseline to a state where `trim_excess_metadata.sh` can be
run and end-to-end tested against a realistic post-divestiture Perforce server.
These scripts automate the manual setup that would otherwise take 20–30 minutes
of careful interactive work. They are intended for:
- Developers and QA engineers iterating on the trim script
- Future AI agents re-running tests after a lab reset
- Documentation of the exact procedure needed to reproduce test conditions
---
## Dependency: Battle School Workshop Lab Environment
**This test harness requires the Battle School Workshop lab.**
Battle School is a Perforce-internal training and dev/test environment that
provides pre-configured, safe-to-play, non-production lab instances running the
full SDP (Server Deployment Package) stack. It is an excellent platform for
developing and validating admin scripts that require sophisticated, realistic
multi-server topologies — without any risk to production data.
### Lab 0 Topology (5 servers, same subnet)
| Host | Role | ServerID | Port |
|---------------|--------------------------|--------------------|------|
| p4c-bos-01 | Commit (master) | commit.p4demo.1 | 1999 |
| p4c-bos-02 | HA standby | p4d_ha_bos | 1999 |
| p4c-nyc-03 | DR standby (sacrificed) | p4d_fs_nyc | 1999 |
| p4c-syd-04 | Edge | p4d_edge_syd | 1999 |
| p4c-syd-05 | Edge HA | p4d_ha_edge_syd | 1999 |
All servers are SDP-managed, SSH-accessible as the `perforce` OS user without a
password (from bos-01 and among themselves).
The lab ships with sample depot data in `//depot/...` (Jam, OpenOffice source,
etc.) and a set of standard users, groups, clients, and jobs.
**Why nyc-03?** It is the DR standby — the most expendable server in the
topology. We transform it into a simulated "divested unit" that was originally
a filtered forwarding replica promoted to a standalone commit server.
---
## What the Setup Simulates
The scenario is a **filtered-replication divestiture**:
1. A company ran a Perforce filtered forwarding replica that only replicated two
depots (`//jam/...` and `//pb/...`) out of many.
2. That replica was promoted to a fully standalone commit server.
3. `trim_excess_metadata.sh` needs to clean up the leftover metadata on that
server: users, clients, groups, labels, branches, jobs, server specs, remote
specs, typemap, triggers, protections, depot specs, and finally snap the
lazy-copy archive links and delete orphaned archive directories.
### Key complexity: lazy-copy archive leakage
Perforce uses lazy copy for branching — `//jam/...` files branched from
`//depot/Jam/...` share physical archive files. When the replica's
RevisionDataFilter keeps `//jam/...` but filters out `//depot/...`, `p4verify`
still pulls the `//depot/...` archive files because `//jam/...` needs them.
After promotion to standalone, `//depot/...` has 0 db.rev records (looks empty)
but `p4 depot -df` refuses to delete it because db.storage still has entries.
The correct resolution is:
1. `p4 snap //jam/... //pb/...` — breaks lazy-copy links (gives kept depots
their own physical archive copies)
2. Journal patch (`p4d -jr`) — removes the depot spec from db.domain
3. `rm -rf` the orphaned archive directories (Phase 18 output)
---
## Scripts
| Script | Runs on | Purpose |
|-------------------------|------------|---------|
| `setup_lab.sh` | p4c-bos-01 | Full Lab 0 → trim-ready setup (orchestrates all steps, SSHes to nyc-03 as needed) |
| `run_trim_test.sh` | p4c-bos-01 | Run trim script: dry run → live → snap; show journal patch apply command |
---
## Quick Start
From **p4c-bos-01** as the `perforce` OS user:
```bash
# 1. Reset lab to Lab 0 baseline (via Battle School UI or reset tool)
# 2. Run setup (takes ~5 minutes; p4verify pulling archives is the slow part)
cd /home/perforce/tem/test
bash setup_lab.sh
# 3. Run the trim test
bash run_trim_test.sh
```
After `run_trim_test.sh` completes, it prints the journal patch path and the
exact `p4d -jr` command to apply it. The patch file must be copied to nyc-03
before applying:
```bash
JNL=/home/perforce/tem/trim_excess_metadata.<timestamp>.jnl
scp "$JNL" perforce@p4c-nyc-03:/tmp/
ssh perforce@p4c-nyc-03 "sudo systemctl stop p4d_1 && \
p4d -r /p4/1/root -jr /tmp/$(basename $JNL) && \
sudo systemctl start p4d_1"
```
Then verify and clean up orphaned archives (rm -rf commands are in Phase 18
output and the trim script summary):
```bash
export P4CONFIG=/home/perforce/tem/.p4config.gf
p4 verify //jam/... //pb/...
ssh perforce@p4c-nyc-03 'rm -rf /p4/1/depots/depot'
```
---
## Expected Test Outcomes
After a successful `run_trim_test.sh`:
| Check | Expected |
|-------|----------|
| `p4 users` | `perforce` only |
| `p4 groups` | `testers` only |
| `p4 depots` | `jam`, `pb`, `Perforce` (remote), `spec`, `unload` |
| `p4 clients` | `perforce`-owned clients only (or none) |
| `p4 servers` | `p4d_commit_gf` only |
| `p4 remotes` | none |
| `p4 typemap` | empty |
| `p4 triggers` | as per `default_triggers.p4s` |
| `p4 verify //jam/... //pb/...` | no errors |
| Journal patch exit code | 0 |
| Trim script exit code (after snap) | 4 (4 shelved CL failures — expected, in filtered depots) |
---
## Keep Files (in /home/perforce/tem/)
The trim script uses two keep files, created by `setup_lab.sh`:
| File | Contents | Purpose |
|------|----------|---------|
| `keep_users.gf.txt` | `perforce` | Only the perforce admin survives the trim |
| `keep_groups.gf.txt` | `testers` | Tests the last-group-member retry logic (Randall_Scott) |
The `testers` group has `Randall_Scott` as its only member. Deleting
`Randall_Scott` would fail with "last member of group testers" — a specific
error the trim script handles by temporarily adding a placeholder owner, then
deleting the user, then cleaning up.
---
## Notes and Gotchas
- **Service user password**: After `mkrep.sh` creates `svc_p4d_ffr_gf`, the
configurable `dm.user.resetpassword=1` marks the account as requiring a
password reset. `setup_lab.sh` sets a known lab password and logs in.
- **`p4d` vs `/p4/1/bin/p4d_1`**: Always use the `p4d` SDP wrapper; it calls
the correct instance binary based on `$SDP_INSTANCE`.
- **Service management**: Use `sudo systemctl start|stop|restart|status p4d_1`
— not manual p4d invocations. `systemctl start` returns immediately; the
service takes ~2–3 seconds to come online.
- **`run.users.authorize=1`**: Do NOT remove this security configurable. The
`setup_lab.sh` ensures the perforce user is logged in before running trim.
- **Server promotion**: nyc-03 gets a *new* ServerID (`p4d_commit_gf`) rather
than reusing `p4d_ffr_gf`. Reusing the old ID would inherit scoped
configurables like `db.replication=readonly`.
- **auth.id**: After promotion, `auth.id` must be unset so the standalone
handles its own authentication.
- **journal patch file location**: `p4d -jr FILE` reads FILE from the local
filesystem of the machine running p4d. If the trim script runs on bos-01
and p4d is on nyc-03, the .jnl file must be SCP'd to nyc-03 first.
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #1 | 32756 | C. Thomas Tyler |
Add test/ directory: setup and run scripts for trim_excess_metadata testing Captures the full Lab 0 -> trim-ready setup procedure as runnable scripts, making it easy for a future agent or human to reproduce the test environment from scratch. Files added: - test/README.md — full documentation: Battle School dependency, lab topology, what the test simulates (filtered-replication divestiture), lazy-copy leakage explanation, expected outcomes, gotchas - test/setup_lab.sh — orchestration script (runs on bos-01); adds gf site tag, runs mkrep.sh, adds RevisionDataFilter/ArchiveDataFilter, fixes service user password, rotates journal, creates filtered seed checkpoint, initialises nyc-03 as FFR replica, pulls archives via p4verify, promotes to standalone commit server, creates .p4config.gf / keep_users / keep_groups files - test/run_trim_test.sh — runs trim in 3 passes (dry, live, snap); prints journal patch apply commands and cleanup instructions Also updated: - ai/session_log_2026-06-16.md — added p4 snap / deep-rename context section Co-authored-by: Copilot <[email protected]> |