Pinpointing the Binlog Position Before an Accidental DELETE
Someone ran DELETE FROM orders without a WHERE, and now recovery hinges on one number: the binlog coordinate immediately before that statement, so a replay can restore every transaction up to it and stop one event short of the damage. Get the coordinate wrong by one transaction and you either re-apply the destructive DELETE or drop a legitimate transaction that committed just before it. This page is the forensic procedure for finding that exact stop coordinate: locate the offending statement in the binary log, read the Gtid and end_log_pos of the transaction before it, and set a --stop-position (or GTID stop) that lands the recovery precisely on the safe side of the boundary.
Context & Prerequisites
This is the surgical-targeting case of Timestamp Targeting Strategies for MySQL Binary Log Archiving and PITR Automation; read that for the general --stop-datetime versus --stop-position model. You need MySQL 8.0.22+ client tools, Python 3.10+, binlog_format=ROW so the DELETE is visible as row events (per ROW vs STATEMENT vs MIXED Formats), and access to the archived segment covering the incident from Automated Binlog Archiving to Object Storage. The resolved coordinate feeds the replay in mysqlbinlog Replay Scripting.
Step-by-Step Implementation
1. Narrow to the segment and rough time window
Use the incident’s approximate time to pick the segment, then decode it to human-readable form for inspection.
# MySQL 8.0.22+ — decode the suspect segment with row events readable
mysqlbinlog --verify-binlog-checksum --base64-output=DECODE-ROWS --verbose \
mysql-bin.000318 > /tmp/incident.sqlPITR relevance: --base64-output=DECODE-ROWS --verbose renders row events as readable pseudo-SQL so you can visually confirm the destructive statement, without producing an applicable file.
2. Locate the offending statement and its Gtid
Search the decoded output for the DELETE (or the table) and note the Gtid line and the end_log_pos that immediately precede it.
# find the destructive event and the boundary just before it
grep -nE "GTID_NEXT|end_log_pos|DELETE FROM .orders." /tmp/incident.sql | lessPITR relevance: the coordinate you want is the end_log_pos at the end of the transaction before the DELETE — replaying to there includes everything good and excludes the damage.
3. Read the exact stop coordinate programmatically
Parse the decoded stream to extract the boundary robustly rather than by eye.
# Python 3.10+
import re
from pathlib import Path
def stop_before(delete_marker: str, decoded: Path) -> tuple[int, str | None]:
last_pos, last_gtid = 4, None
for line in decoded.read_text().splitlines():
if delete_marker in line:
return last_pos, last_gtid # boundary just before the DELETE
if (m := re.search(r"end_log_pos (\d+)", line)):
last_pos = int(m.group(1))
if (g := re.search(r"SET @@SESSION.GTID_NEXT= '([^']+)'", line)):
last_gtid = g.group(1)
raise ValueError("destructive statement not found in segment")PITR relevance: capturing both the position and the preceding GTID gives you two equivalent stop conditions — prefer the GTID for exactness, fall back to the position within the segment.
4. Recover up to the boundary on an isolated target
Replay to the resolved stop coordinate on an isolated target, verify the deleted rows are present, then promote.
# MySQL 8.0.22+ — replay everything up to (not including) the DELETE
mysqlbinlog --verify-binlog-checksum --stop-position=25120 \
mysql-bin.000318 | mysql --host=recovery-targetPITR relevance: stopping one event before the DELETE reconstructs the table exactly as it was the instant before the mistake, which is the entire objective of surgical PITR.
Configuration Snippet & Reference Table
| Coordinate | How to read it | Use as |
|---|---|---|
| offending statement | grep the decoded stream | the boundary marker |
preceding end_log_pos | last end_log_pos before it | --stop-position |
| preceding GTID | last GTID_NEXT before it | GTID stop (exact) |
| decode flags | --base64-output=DECODE-ROWS --verbose | inspection only |
| apply flags | --stop-position=<n> | the actual recovery |
Verification Checklist
Gotchas & Version-Specific Caveats
--base64-output=DECODE-ROWS is for reading only. It renders row events as comments for inspection; a file produced with it is not applicable. Apply with the normal decode (no DECODE-ROWS) and the resolved --stop-position.
Off-by-one lands on the wrong side. Using the DELETE’s own end_log_pos instead of the preceding transaction’s includes the damage. Always take the boundary before the offending GTID.
Concurrent transactions share seconds. A --stop-datetime cannot distinguish the DELETE from a legitimate transaction that committed in the same second; use the position or GTID for surgical precision.
The event may span segments. If the incident sits near a rotation boundary, the preceding transaction may be in the prior segment; decode both and resolve across the boundary.
Frequently Asked Questions
Should I stop by position or by GTID?
Prefer the GTID of the last good transaction when you can name it — it is exact and immune to clock skew or events sharing a commit second. Use --stop-position as the concrete within-segment equivalent, which is what mysqlbinlog accepts directly. Capturing both during the forensic step gives you the exact stop and a fallback, and they should identify the same boundary.
How do I confirm I stopped in the right place?
Replay to the resolved coordinate on an isolated target and check two things: the rows the DELETE removed are present (you stopped before it), and the DELETE’s effect is absent (you did not replay past it). If both hold, the boundary is correct and the target can be promoted. Never validate on the production primary — always recover to an isolated instance first, per mysqlbinlog Replay Scripting.
Related
- Timestamp Targeting Strategies — the general model of choosing a stop condition.
- mysqlbinlog Replay Scripting — replaying to the resolved coordinate on an isolated target.
- Automating mysqlbinlog Replay with Stop-Position Checkpoints — turning the coordinate into a resumable replay.
Back to Timestamp Targeting Strategies.