Skip to content

Commit c2438e0

Browse files
Add partial backup option
1 parent 5c5bf8f commit c2438e0

2 files changed

Lines changed: 93 additions & 22 deletions

File tree

installation_and_upgrade/part_truncate_archive.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ IF %errorlevel% neq 0 (
1515
REM Matches current MySQL version
1616
uv pip install mysql-connector-python==8.4.0
1717

18-
python -u part_truncate_archive.py %*
18+
python -u %~dp0part_truncate_archive.py %*
1919
IF %errorlevel% neq 0 goto ERROR
2020
call rmdir /s /q %UV_TEMP_VENV%
2121

installation_and_upgrade/part_truncate_archive.py

Lines changed: 92 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import argparse
2+
import datetime
3+
import subprocess
24
import time
35
from contextlib import closing
46

@@ -41,60 +43,129 @@ def main() -> None:
4143
default=0.5,
4244
)
4345
parser.add_argument(
44-
"--history",
45-
dest="history",
46+
"--days",
47+
dest="days",
4648
action="store",
4749
type=int,
4850
help="How many days to keep (default: 7)",
4951
default=7,
5052
)
5153
parser.add_argument(
52-
"--password", dest="password", action="store", help="mysql root password", default=""
54+
"--hours",
55+
dest="hours",
56+
action="store",
57+
type=int,
58+
help="How many hours to keep (default: 0)",
59+
default=0,
60+
)
61+
parser.add_argument(
62+
"--minutes",
63+
dest="minutes",
64+
action="store",
65+
type=int,
66+
help="How many minutes to keep (default: 0)",
67+
default=0,
68+
)
69+
parser.add_argument("--user", dest="user", action="store", help="mysql user", default="root")
70+
parser.add_argument(
71+
"--password", dest="password", action="store", help="mysql password", default=""
72+
)
73+
parser.add_argument(
74+
"--backup", dest="backup", action="store", help="backup data before deleting to file"
5375
)
5476
parser.add_argument("--dry-run", dest="dry_run", action="store_true", help="dry run")
5577

5678
args = parser.parse_args()
57-
79+
sample_id_max = None
80+
sample_id_min = None
81+
count_sample_id = None
82+
time_interval = datetime.timedelta(days=args.days, hours=args.hours, minutes=args.minutes)
83+
cutoff_time = datetime.datetime.now() - time_interval
84+
cutoff_isotime = cutoff_time.isoformat(" ", "seconds")
5885
# ignore pyright checking as oracle bug in type signature of close() method
5986
with closing(
6087
mysql.connector.connect(
61-
user="root", password=args.password, host=args.host, database="archive"
88+
user=args.user, password=args.password, host=args.host, database="archive"
6289
) # pyright: ignore
6390
) as conn:
6491
# this is so we don't cache query results and keep getting the same answer
6592
conn.autocommit = True
6693

6794
with closing(conn.cursor(prepared=True)) as c:
68-
c.execute("SET SQL_LOG_BIN=0") # disable any binary logging for this session
69-
print(f"Looking for sample_id corresponding to {args.history} days ago")
70-
c.execute(
71-
"SELECT MAX(sample_id) FROM sample WHERE smpl_time < TIMESTAMPADD(DAY, -?, NOW())",
72-
(args.history,),
73-
)
74-
sample_id = c.fetchone()[0]
75-
c.execute(
76-
"SELECT COUNT(sample_id) FROM sample "
77-
"WHERE smpl_time < TIMESTAMPADD(DAY, -?, NOW())",
78-
(args.history,),
79-
)
95+
print(f"Looking for sample_id corresponding to {cutoff_isotime}")
96+
c.execute(f"SELECT MAX(sample_id) FROM sample WHERE smpl_time < '{cutoff_isotime}'")
97+
sample_id_max = c.fetchone()[0]
98+
c.execute(f"SELECT MIN(sample_id) FROM sample WHERE smpl_time < '{cutoff_isotime}'")
99+
sample_id_min = c.fetchone()[0]
100+
c.execute(f"SELECT COUNT(sample_id) FROM sample WHERE smpl_time < '{cutoff_isotime}'")
80101
count_sample_id = c.fetchone()[0]
81102
print(
82-
f"ID of last row to delete is {sample_id} and there are {count_sample_id} rows "
103+
f"ID range to delete is {sample_id_min} to {sample_id_max} "
104+
f"and there are {count_sample_id} rows "
83105
f"-> {int(1 + count_sample_id / args.limit)} delete operations"
84106
)
107+
108+
if args.backup:
109+
command = [
110+
r"C:\Instrument\Apps\MySQL\bin\mysqldump.exe",
111+
f"--user={args.user}",
112+
f"--password={args.password}",
113+
f"--host={args.host}",
114+
"--single-transaction",
115+
f"--result-file={args.backup}",
116+
"--no-create-db",
117+
"--no-create-info",
118+
"--skip-triggers",
119+
"--quick",
120+
f"--where=sample_id >= {sample_id_min} AND sample_id <= {sample_id_max} "
121+
f"AND smpl_time < '{cutoff_isotime}'",
122+
"archive",
123+
"sample",
124+
]
125+
if args.dry_run:
126+
print(command)
127+
else:
128+
subprocess.run(command, check=True)
129+
130+
# ignore pyright checking as oracle bug in type signature of close() method
131+
with closing(
132+
mysql.connector.connect(
133+
user=args.user, password=args.password, host=args.host, database="archive"
134+
) # pyright: ignore
135+
) as conn:
136+
# this is so we don't cache query results and keep getting the same answer
137+
conn.autocommit = True
138+
139+
with closing(conn.cursor(prepared=True)) as c:
140+
c.execute("SET SQL_LOG_BIN=0") # disable any binary logging for this session
141+
delete_ops = int(1 + count_sample_id / args.limit)
142+
143+
print(
144+
f"ID range to delete is {sample_id_min} to {sample_id_max} "
145+
f"and there are {count_sample_id} rows "
146+
f"-> {delete_ops} delete operations"
147+
)
85148
print(
86-
f"This will take at least {args.sleep * count_sample_id / args.limit:.1f} "
149+
f"This will take at least {args.sleep * delete_ops:.1f} "
87150
"seconds based on sleep time alone"
88151
)
89152
if args.dry_run:
90153
print("Exiting as dry-run")
91154
return
92155
rowcount = 1
93156
it = 0
157+
progress = 0
94158
while rowcount > 0:
95-
c.execute(f"DELETE FROM sample WHERE sample_id < {sample_id} LIMIT {args.limit}")
159+
c.execute(
160+
f"DELETE FROM sample WHERE sample_id >= {sample_id_min} AND "
161+
f"sample_id <= {sample_id_max} AND smpl_time < '{cutoff_isotime}' "
162+
f"LIMIT {args.limit}"
163+
)
96164
rowcount = c.rowcount
97-
print(f"{it % 10}", end="", flush=True)
165+
so_far = 100.0 * it / delete_ops
166+
if so_far > progress:
167+
print(f"{progress}% ", end="", flush=True)
168+
progress += 5
98169
it += 1
99170
time.sleep(args.sleep)
100171
print("")

0 commit comments

Comments
 (0)