Skip to content

Commit 3db2c4f

Browse files
authored
Fix metadata files not being cleaned (#659)
Fixes #658
1 parent 536ae14 commit 3db2c4f

7 files changed

Lines changed: 47 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ Don't forget to remove deprecated code on each major release!
1414

1515
## [Unreleased]
1616

17-
- Nothing (yet)!
17+
### Fixed
18+
19+
- Fixed a bug where metadata files were not being removed during clean-up.
1820

1921
## [5.1.1] - 2026-01-07
2022

dbbackup/storage.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ def clean_old_backups(
281281
continue
282282
self.delete_file(filename)
283283

284+
metadata_filename = f"{filename}.metadata"
285+
if self.storage.exists(metadata_filename):
286+
self.delete_file(metadata_filename)
287+
284288

285289
def get_storage_class(path=None):
286290
"""

docs/src/commands.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Backup size: 3.3 KiB
1919
Writing file to tmp-zuluvm-2016-07-29-100954.dump
2020
```
2121

22-
For detailed help information, run:
22+
For parameters and more information, run:
2323

2424
```bash
2525
python manage.py dbbackup --help
@@ -37,7 +37,7 @@ Restoring: tmp-zuluvm-2016-07-29-100954.dump
3737
Restore tempfile created: 3.3 KiB
3838
```
3939

40-
For detailed help information, run:
40+
For parameters and more information, run:
4141

4242
```bash
4343
python manage.py dbrestore --help
@@ -54,7 +54,7 @@ Backup size: 10.0 KiB
5454
Writing file to zuluvm-2016-07-04-081612.tar
5555
```
5656

57-
For detailed help information, run:
57+
For parameters and more information, run:
5858

5959
```bash
6060
python manage.py mediabackup --help
@@ -75,7 +75,7 @@ Are you sure you want to continue? [Y/n]
7575
2 file(s) restored
7676
```
7777

78-
For detailed help information, run:
78+
For parameters and more information, run:
7979

8080
```bash
8181
python manage.py mediarestore --help
@@ -85,7 +85,7 @@ python manage.py mediarestore --help
8585

8686
This command lists backups filtered by type (`'media'` or `'db'`), compression, or encryption.
8787

88-
For detailed help information, run:
88+
For parameters and more information, run:
8989

9090
```bash
9191
python manage.py listbackups --help

docs/src/configuration.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,9 @@ when using `--encrypt` and for automatic decryption with `--decrypt`.
142142

143143
### DBBACKUP_CONNECTORS
144144

145-
Optional per database override mapping similar to `DATABASES`. Lets you define
146-
different credentials / hosts exclusively for backup / restore operations
145+
Allows for overriding the connector used for specific Django database(s).
146+
This mapping is similar to `DATABASES`. Lets you define different
147+
credentials / hosts exclusively for backup / restore operations
147148
(e.g. read from a replica). Keys correspond to database aliases.
148149

149150
See the [Databases](databases.md) section for details on how to configure
@@ -154,9 +155,9 @@ Default: `{}`
154155
### DBBACKUP_CONNECTOR_MAPPING
155156

156157
Map custom database engine names (e.g. from wrappers like `transaction_hooks`
157-
or third party observability packages) to an existing DBBackup connector class path.
158-
Useful when a third party backend subclasses a supported Django backend but
159-
uses a different engine string.
158+
or third party observability packages) to a DBBackup connector class.
159+
Useful when you are using a third party backend that subclasses a supported
160+
Django database backend.
160161

161162
See the [Databases](databases.md) section for details on how to configure the
162163
connector mapping.

docs/src/databases.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ below.
3434

3535
All connectors have the following parameters:
3636

37-
| Setting | Description | Default |
38-
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
39-
| CONNECTOR | Absolute path to connector class. Defaults by engine: `dbbackup.db.sqlite.SqliteBackupConnector` (sqlite3), `dbbackup.db.mysql.MysqlDumpConnector` (mysql), `dbbackup.db.postgresql.PgDumpConnector` (postgresql), `dbbackup.db.postgresql.PgDumpGisConnector` (postgis), `dbbackup.db.mongodb.MongoDumpConnector` (django_mongodb_engine), `dbbackup.db.django.DjangoConnector` (fallback / any unmapped). Prometheus wrappers are also supported mapping to the same connectors. | Auto-detected from `ENGINE` |
40-
| EXCLUDE | List of table names to exclude from dump (may be unsupported for raw file copy snapshot approaches). Example below. | None |
41-
| EXTENSION | File extension used for the generated dump archive. | `dump` |
37+
| Setting | Description | Default |
38+
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
39+
| CONNECTOR | Absolute path to connector class. For example: `dbbackup.db.django.DjangoConnector`. Prometheus wrappers are also supported mapping to the same connectors. | Auto-detected from `ENGINE` |
40+
| EXCLUDE | List of table names to exclude from dump (may be unsupported for raw file copy snapshot approaches). Example below. | None |
41+
| EXTENSION | File extension used for the generated dump archive. | `dump` |
4242

4343
Example for `EXCLUDE` usage:
4444

@@ -95,7 +95,7 @@ This connector can be used to restore a backup to an existing (dirty) database d
9595

9696
The `dbbackup.db.sqlite.SqliteCPConnector` connector can be used to make a simple raw copy of your database file, like a snapshot.
9797

98-
In-memory databases are **not** dumpable with it. Since it works by copying the database file directly, it is not suitable for databases that are have active connections.
98+
In-memory databases are **not** dumpable with it. Since it works by copying the database file directly, it is not suitable for databases that have ongoing connections.
9999

100100
### MySQL
101101

@@ -214,7 +214,7 @@ The Django connector is ideal for:
214214
### Limitations
215215

216216
- **Performance**: Slower than native database tools for large datasets
217-
- **Database structure**: Only backs up data, not database schema, indices, or procedures
217+
- **Database structure**: Only provides backups of data; not database schema, indices, or procedures
218218

219219
### File Extension
220220

tests/test_storage.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,27 @@ def test_keep_filter(self):
220220
self.storage.clean_old_backups(keep_number=1)
221221
assert HANDLED_FILES["deleted_files"] == ["2015-02-07-042810.bak"]
222222

223+
def test_clean_includes_metadata(self):
224+
for f in [
225+
"2015-02-06-042810.bak.metadata",
226+
"2015-02-07-042810.bak.metadata",
227+
"2015-02-08-042810.bak.metadata",
228+
]:
229+
HANDLED_FILES["written_files"].append((f, None))
230+
231+
self.storage.clean_old_backups(keep_number=1)
232+
233+
deleted_files = sorted(HANDLED_FILES["deleted_files"])
234+
expected_deleted = sorted(
235+
[
236+
"2015-02-06-042810.bak",
237+
"2015-02-06-042810.bak.metadata",
238+
"2015-02-07-042810.bak",
239+
"2015-02-07-042810.bak.metadata",
240+
]
241+
)
242+
self.assertEqual(deleted_files, expected_deleted)
243+
223244

224245
class StorageEdgeCasesTest(TestCase):
225246
@patch("dbbackup.settings.STORAGE", "")

tests/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class FakeStorage(Storage):
6464
name = "FakeStorage"
6565

6666
def exists(self, name):
67-
return name in HANDLED_FILES["written_files"]
67+
return any(f[0] == name for f in HANDLED_FILES["written_files"])
6868

6969
def get_available_name(self, name, max_length=None):
7070
return name[:max_length]

0 commit comments

Comments
 (0)