Skip to content

Commit e5aad27

Browse files
Add database support script
1 parent c56d906 commit e5aad27

File tree

1 file changed

+103
-0
lines changed

1 file changed

+103
-0
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import argparse
2+
import time
3+
from contextlib import closing
4+
5+
import mysql.connector
6+
7+
"""
8+
This script will delete rows from the mysql archive `sample` table
9+
that are more than `history` days old in batches of `limit` to improve performance.
10+
11+
This could be done after a database backup and be used instead of truncating
12+
the entire database. It will not reduce the size of the database file on disk, but the
13+
deleted rows will be re-used by the database and so the file should not grow.
14+
15+
A typical usage would be if the instrument already has information in mysql it would like
16+
to keep a bit longer, or is running. The backup is safe to do on a running as it uses
17+
`single transaction` mode, but if the backup takes 2 hours you'll have two hours of missed
18+
data after a truncate. Hence this script to leave some old data in mysql.
19+
"""
20+
21+
22+
def main() -> None:
23+
parser = argparse.ArgumentParser(description="Set query options")
24+
parser.add_argument(
25+
"--host", dest="host", action="store", help="Host (default: localhost)", default="127.0.0.1"
26+
)
27+
parser.add_argument(
28+
"--limit",
29+
dest="limit",
30+
action="store",
31+
type=int,
32+
help="Rows to delete each query (default: 1000)",
33+
default=1000,
34+
)
35+
parser.add_argument(
36+
"--sleep",
37+
dest="sleep",
38+
action="store",
39+
type=float,
40+
help="Seconds to sleep between queries (default: 0.5)",
41+
default=0.5,
42+
)
43+
parser.add_argument(
44+
"--history",
45+
dest="history",
46+
action="store",
47+
type=int,
48+
help="How many days to keep (default: 7)",
49+
default=7,
50+
)
51+
parser.add_argument(
52+
"--password", dest="password", action="store", help="mysql root password", default=""
53+
)
54+
parser.add_argument("--dry-run", dest="dry_run", action="store_true", help="dry run")
55+
56+
args = parser.parse_args()
57+
58+
with closing(
59+
mysql.connector.connect(
60+
user="root", password=args.password, host=args.host, database="archive"
61+
)
62+
) as conn:
63+
# this is so we don't cache query results and keep getting the same answer
64+
conn.autocommit = True
65+
66+
with closing(conn.cursor(prepared=True)) as c:
67+
c.execute("SET SQL_LOG_BIN=0") # disable any binary logging for this session
68+
print(f"Looking for sample_id corresponding to {args.history} days ago")
69+
c.execute(
70+
"SELECT MAX(sample_id) FROM sample WHERE smpl_time < TIMESTAMPADD(DAY, -?, NOW())",
71+
(args.history,),
72+
)
73+
sample_id = c.fetchone()[0]
74+
c.execute(
75+
"SELECT COUNT(sample_id) FROM sample "
76+
"WHERE smpl_time < TIMESTAMPADD(DAY, -?, NOW())",
77+
(args.history,),
78+
)
79+
count_sample_id = c.fetchone()[0]
80+
print(
81+
f"ID of last row to delete is {sample_id} and there are {count_sample_id} rows "
82+
f"-> {int(1 + count_sample_id / args.limit)} delete operations"
83+
)
84+
print(
85+
f"This will take at least {args.sleep * count_sample_id / args.limit:.1f} "
86+
"seconds based on sleep time alone"
87+
)
88+
if args.dry_run:
89+
print("Exiting as dry-run")
90+
return
91+
rowcount = 1
92+
it = 0
93+
while rowcount > 0:
94+
c.execute(f"DELETE FROM sample WHERE sample_id < {sample_id} LIMIT {args.limit}")
95+
rowcount = c.rowcount
96+
print(f"{it % 10}", end="", flush=True)
97+
it += 1
98+
time.sleep(args.sleep)
99+
print("")
100+
101+
102+
if __name__ == "__main__":
103+
main()

0 commit comments

Comments
 (0)