discogskit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,353 @@
1
+ Metadata-Version: 2.4
2
+ Name: discogskit
3
+ Version: 0.1.0
4
+ Summary: Discogs Data Dumps Toolkit
5
+ Keywords: cli,discogs,discogs-dump,music-database
6
+ Author: Jean-Marc Fontaine
7
+ Author-email: Jean-Marc Fontaine <jm@jmfontaine.net>
8
+ License-Expression: Apache-2.0
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Database
19
+ Requires-Dist: adbc-driver-postgresql>=1.10.0
20
+ Requires-Dist: lxml>=6.0.2
21
+ Requires-Dist: psycopg>=3.3.3
22
+ Requires-Dist: pyarrow>=23.0.1
23
+ Requires-Dist: rapidgzip>=0.16.0
24
+ Requires-Dist: rich>=14.3.3
25
+ Requires-Dist: typer>=0.24.1
26
+ Requires-Python: >=3.10
27
+ Description-Content-Type: text/markdown
28
+
29
+ # discogskit
30
+
31
+ A fast tool for converting and loading [Discogs data dumps](https://www.discogs.com/data/) into Parquet, JSONL, SQLite,
32
+ and PostgreSQL.
33
+
34
+ ## Why discogskit?
35
+
36
+ - **Fast.** Parallel parsing and writing squeeze maximum performance out of your machine. See [Benchmarks](#benchmarks)
37
+ for numbers.
38
+ - **Easy to use.** A single command does the job. No multi-step workflows, no manual schema setup.
39
+ - **Flexible outputs.** Convert to Parquet or JSONL for quick analysis without standing up a database, or load directly
40
+ into SQLite or PostgreSQL.
41
+ - **Reliable.** Comprehensive unit and integration tests run against every release.
42
+
43
+ ## Installation
44
+
45
+ Requires Python 3.10+.
46
+
47
+ ```bash
48
+ pipx install discogskit
49
+ ```
50
+
51
+ Or with [uv](https://docs.astral.sh/uv/):
52
+
53
+ ```bash
54
+ # Install locally
55
+ uv tool install discogskit
56
+
57
+ # Run without installing
58
+ uvx discogskit
59
+ ```
60
+
61
+ ## Usage
62
+
63
+ ```text
64
+
65
+ Usage: discogskit [OPTIONS] COMMAND [ARGS]...
66
+
67
+ discogskit: Discogs Data Dumps Toolkit
68
+
69
+ ╭─ Options ────────────────────────────────────────────────────────────────────────────╮
70
+ │ --version Show version and exit. │
71
+ │ --install-completion Install completion for the current shell. │
72
+ │ --show-completion Show completion for the current shell, to copy it or │
73
+ │ customize the installation. │
74
+ │ --help Show this message and exit. │
75
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
76
+ ╭─ Commands ───────────────────────────────────────────────────────────────────────────╮
77
+ │ convert Convert Discogs XML dumps into flat files (Parquet or JSONL). │
78
+ │ load Load Discogs XML dumps into a database. │
79
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
80
+
81
+ ```
82
+
83
+ ### discogskit convert
84
+
85
+ Convert Discogs XML dumps into flat files.
86
+
87
+ | Option | Values |
88
+ |-----------------|-------------------------------|
89
+ | Output formats | `parquet`, `jsonl` |
90
+ | Compression (Parquet) | `zstd` (default), `snappy`, `gzip`, `none` |
91
+ | Compression (JSONL) | `gzip`, `bzip2`, `none` (default) |
92
+
93
+ <details>
94
+ <summary>Full command help</summary>
95
+
96
+ ```text
97
+
98
+ Usage: discogskit convert [OPTIONS] PATHS...
99
+
100
+ Convert Discogs XML dumps into flat files (Parquet or JSONL).
101
+
102
+ ╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
103
+ │ * paths PATHS... One or more .xml.gz files or directories containing them │
104
+ │ [required] │
105
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
106
+ ╭─ Options ────────────────────────────────────────────────────────────────────────────╮
107
+ │ --format -f TEXT Output format: parquet or jsonl │
108
+ │ [default: parquet] │
109
+ │ --output PATH Output directory [default: .] │
110
+ │ --compression TEXT Compression codec. Parquet: gzip, │
111
+ │ snappy, zstd (default), none. JSONL: │
112
+ │ bzip2, gzip, none (default). │
113
+ │ --parse-workers INTEGER Number of parallel parse workers │
114
+ │ [default: 4] │
115
+ │ --chunk-mb INTEGER Split XML into chunks of roughly │
116
+ │ this size (MB) │
117
+ │ [default: 256] │
118
+ │ --write-queue INTEGER Max chunks buffered in memory before │
119
+ │ writes must catch up │
120
+ │ [default: 2] │
121
+ │ --keep-xml --no-keep-xml Keep decompressed XML file after │
122
+ │ converting │
123
+ │ [default: no-keep-xml] │
124
+ │ --overwrite --no-overwrite Overwrite existing output files │
125
+ │ [default: no-overwrite] │
126
+ │ --profile --no-profile Print detailed per-table timing │
127
+ │ breakdown after convert │
128
+ │ [default: no-profile] │
129
+ │ --progress --no-progress Show a progress bar instead of │
130
+ │ per-chunk output │
131
+ │ [default: progress] │
132
+ │ --strict --no-strict Warn about unhandled XML elements │
133
+ │ during parsing │
134
+ │ [default: no-strict] │
135
+ │ --help Show this message and exit. │
136
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
137
+
138
+ ```
139
+
140
+ </details>
141
+
142
+ #### Examples
143
+
144
+ ```shell
145
+ # Convert releases to Parquet (default)
146
+ discogskit convert --format parquet discogs_20260301_releases.xml.gz
147
+
148
+ # Convert to JSONL with gzip compression
149
+ discogskit convert --format jsonl --compression gzip discogs_20260301_artists.xml.gz
150
+
151
+ # Convert all dump files in the current directory
152
+ discogskit convert --format parquet .
153
+
154
+ # Keep decompressed XML after converting
155
+ discogskit convert --format parquet --keep-xml discogs_20260301_releases.xml.gz
156
+ ```
157
+
158
+ ### discogskit load
159
+
160
+ Load Discogs XML dumps into a database.
161
+
162
+ | Database | Versions |
163
+ |----------|----------|
164
+ | SQLite | 3.x |
165
+ | PostgreSQL | 14+ |
166
+
167
+ <details>
168
+ <summary>Full command help</summary>
169
+
170
+ ```text
171
+
172
+ Usage: discogskit load [OPTIONS] PATHS...
173
+
174
+ Load Discogs XML dumps into a database.
175
+
176
+ ╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
177
+ │ * paths PATHS... One or more .xml.gz files or directories containing them │
178
+ │ [required] │
179
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
180
+ ╭─ Options ────────────────────────────────────────────────────────────────────────────╮
181
+ │ --dsn TEXT Database DSN (e.g., │
182
+ │ postgresql://localhost/postgres) or │
183
+ │ path to SQLite file │
184
+ │ [env var: DATABASE_URL] │
185
+ │ [default: │
186
+ │ postgresql://localhost/discogskit] │
187
+ │ --parse-workers INTEGER Number of parallel parse workers │
188
+ │ [default: 4] │
189
+ │ --write-workers INTEGER Number of parallel database write │
190
+ │ workers │
191
+ │ [default: 1] │
192
+ │ --index-workers INTEGER Number of parallel index creation │
193
+ │ workers │
194
+ │ [default: 2] │
195
+ │ --chunk-mb INTEGER Split XML into chunks of roughly this │
196
+ │ size (MB) │
197
+ │ [default: 256] │
198
+ │ --write-queue INTEGER Max chunks buffered in memory before │
199
+ │ writes must catch up │
200
+ │ [default: 2] │
201
+ │ --keep-xml --no-keep-xml Keep decompressed XML file after │
202
+ │ loading │
203
+ │ [default: no-keep-xml] │
204
+ │ --overwrite --no-overwrite Overwrite existing tables in the │
205
+ │ database │
206
+ │ [default: no-overwrite] │
207
+ │ --profile --no-profile Print detailed per-table timing │
208
+ │ breakdown after load │
209
+ │ [default: no-profile] │
210
+ │ --progress --no-progress Show a progress bar instead of │
211
+ │ per-chunk output │
212
+ │ [default: progress] │
213
+ │ --strict --no-strict Warn about unhandled XML elements │
214
+ │ during parsing │
215
+ │ [default: no-strict] │
216
+ │ --help Show this message and exit. │
217
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
218
+ ╭─ PostgreSQL ─────────────────────────────────────────────────────────────────────────╮
219
+ │ --pg-unlogged --no-pg-unlogged Skip WAL for faster writes (tables stay │
220
+ │ unlogged; data lost on crash) │
221
+ │ [default: no-pg-unlogged] │
222
+ │ --pg-tune --no-pg-tune Temporarily apply settings optimized for bulk │
223
+ │ loading │
224
+ │ [default: no-pg-tune] │
225
+ │ --pg-fk --no-pg-fk Add foreign key constraints after load │
226
+ │ [default: no-pg-fk] │
227
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
228
+
229
+ ```
230
+
231
+ </details>
232
+
233
+ #### Examples
234
+
235
+ ```shell
236
+ # Load releases into PostgreSQL (default DSN: postgresql://localhost/discogskit)
237
+ discogskit load discogs_20260301_releases.xml.gz
238
+
239
+ # Load into a specific PostgreSQL database
240
+ discogskit load --dsn "postgresql://user:pass@localhost/discogs" discogs_20260301_releases.xml.gz
241
+
242
+ # Load into SQLite
243
+ discogskit load --dsn discogs.db discogs_20260301_releases.xml.gz
244
+
245
+ # Load all dump files from a directory
246
+ discogskit load --dsn discogs.db .
247
+
248
+ # Use UNLOGGED tables for faster PostgreSQL writes (~2x speedup)
249
+ discogskit load --pg-unlogged discogs_20260301_releases.xml.gz
250
+
251
+ # Temporarily tune PostgreSQL for bulk loading
252
+ discogskit load --pg-tune discogs_20260301_releases.xml.gz
253
+
254
+ # Add foreign key constraints after load
255
+ discogskit load --pg-fk discogs_20260301_releases.xml.gz
256
+
257
+ # Use multiple write workers for parallel database inserts
258
+ discogskit load --write-workers 4 discogs_20260301_releases.xml.gz
259
+ ```
260
+
261
+ ## Benchmarks
262
+
263
+ Full load of the `20260301` data dump (artists, labels, masters, releases) into PostgreSQL 18
264
+ on a 24 GB Apple MacBook Air M3.
265
+
266
+ | | discogs-xml2db Python | discogs-xml2db .NET | discogskit | discogskit `--pg-unlogged` |
267
+ |---|---:|---:|---:|---:|
268
+ | Parse + load | 0:59:11 | 1:01:08 | 18:55 | 9:25 |
269
+ | Indexes | 0:43:24 | 0:43:24 | 14:53 | 1:56 |
270
+ | **Total** | **1:42:35** | **1:44:32** | **33:49** | **11:22** |
271
+ | **Speedup** | **baseline** | **0.98x** | **3.0x** | **9.0x** |
272
+
273
+ <details>
274
+ <summary>Commands and detailed output</summary>
275
+
276
+ **discogs-xml2db Python**
277
+
278
+ ```shell
279
+ python3 run.py --apicounts --export artist --export label --export master --export release --output ./csv-dir [path]
280
+ python3 postgresql/psql.py < postgresql/sql/CreateTables.sql
281
+ python3 postgresql/importcsv.py ./csv-dir/*
282
+ python3 postgresql/psql.py < postgresql/sql/CreatePrimaryKeys.sql
283
+ python3 postgresql/psql.py < postgresql/sql/CreateFKConstraints.sql
284
+ python3 postgresql/psql.py < postgresql/sql/CreateIndexes.sql
285
+ ```
286
+
287
+ | Step | Time |
288
+ |---|---:|
289
+ | Export to CSV | 0:41:14 |
290
+ | Table creation | 0:00:01 |
291
+ | Data import | 0:17:56 |
292
+ | Primary keys | 0:19:21 |
293
+ | Foreign keys | 0:02:12 |
294
+ | Indexes | 0:21:51 |
295
+ | **Total** | **1:42:35** |
296
+
297
+ **discogs-xml2db .NET**
298
+
299
+ ```shell
300
+ discogs [paths]
301
+ python3 postgresql/psql.py < postgresql/sql/CreateTables.sql
302
+ python3 postgresql/importcsv.py ./csv-dir/*
303
+ python3 postgresql/psql.py < postgresql/sql/CreatePrimaryKeys.sql
304
+ python3 postgresql/psql.py < postgresql/sql/CreateFKConstraints.sql
305
+ python3 postgresql/psql.py < postgresql/sql/CreateIndexes.sql
306
+ ```
307
+
308
+ | Step | Time |
309
+ |---|---:|
310
+ | Export to CSV | 0:43:11 |
311
+ | Table creation | 0:00:01 |
312
+ | Data import | 0:17:56 |
313
+ | Primary keys | 0:19:21 |
314
+ | Foreign keys | 0:02:12 |
315
+ | Indexes | 0:21:51 |
316
+ | **Total** | **1:44:32** |
317
+
318
+ **discogskit**
319
+
320
+ ```shell
321
+ discogskit load --dsn postgresql://localhost:5432/discogskit --chunk-mb 256 \
322
+ --parse-workers 6 --write-workers 3 --index-workers 6 [path]
323
+ ```
324
+
325
+ | Entity | Records | Parse + load | Indexes | Total |
326
+ |---|---:|---:|---:|---:|
327
+ | Artists | 9,957,079 | 40.40s | 14.77s | 55.15s |
328
+ | Labels | 2,349,729 | 9.27s | 0.67s | 9.95s |
329
+ | Masters | 2,530,697 | 34.45s | 19.94s | 54.36s |
330
+ | Releases | 18,952,204 | 1,051.37s | 857.89s | 1,909.24s |
331
+ | **Total** | **33,789,709** | **1,135.49s** | **893.27s** | **2,028.70s** |
332
+
333
+ **discogskit `--pg-unlogged`**
334
+
335
+ ```shell
336
+ discogskit load --dsn postgresql://localhost:5432/discogskit --chunk-mb 256 --pg-unlogged \
337
+ --parse-workers 6 --write-workers 3 --index-workers 6 [path]
338
+ ```
339
+
340
+ | Entity | Records | Parse + load | Indexes | Total |
341
+ |---|---:|---:|---:|---:|
342
+ | Artists | 9,957,079 | 27.73s | 2.52s | 30.22s |
343
+ | Labels | 2,349,729 | 8.84s | 0.51s | 9.34s |
344
+ | Masters | 2,530,697 | 23.38s | 1.93s | 25.29s |
345
+ | Releases | 18,952,204 | 505.45s | 111.46s | 616.86s |
346
+ | **Total** | **33,789,709** | **565.40s** | **116.42s** | **681.71s** |
347
+
348
+ </details>
349
+
350
+
351
+ ## License
352
+
353
+ discogskit is licensed under the [Apache License 2.0](LICENSE.txt).
@@ -0,0 +1,325 @@
1
+ # discogskit
2
+
3
+ A fast tool for converting and loading [Discogs data dumps](https://www.discogs.com/data/) into Parquet, JSONL, SQLite,
4
+ and PostgreSQL.
5
+
6
+ ## Why discogskit?
7
+
8
+ - **Fast.** Parallel parsing and writing squeeze maximum performance out of your machine. See [Benchmarks](#benchmarks)
9
+ for numbers.
10
+ - **Easy to use.** A single command does the job. No multi-step workflows, no manual schema setup.
11
+ - **Flexible outputs.** Convert to Parquet or JSONL for quick analysis without standing up a database, or load directly
12
+ into SQLite or PostgreSQL.
13
+ - **Reliable.** Comprehensive unit and integration tests run against every release.
14
+
15
+ ## Installation
16
+
17
+ Requires Python 3.10+.
18
+
19
+ ```bash
20
+ pipx install discogskit
21
+ ```
22
+
23
+ Or with [uv](https://docs.astral.sh/uv/):
24
+
25
+ ```bash
26
+ # Install locally
27
+ uv tool install discogskit
28
+
29
+ # Run without installing
30
+ uvx discogskit
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```text
36
+
37
+ Usage: discogskit [OPTIONS] COMMAND [ARGS]...
38
+
39
+ discogskit: Discogs Data Dumps Toolkit
40
+
41
+ ╭─ Options ────────────────────────────────────────────────────────────────────────────╮
42
+ │ --version Show version and exit. │
43
+ │ --install-completion Install completion for the current shell. │
44
+ │ --show-completion Show completion for the current shell, to copy it or │
45
+ │ customize the installation. │
46
+ │ --help Show this message and exit. │
47
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
48
+ ╭─ Commands ───────────────────────────────────────────────────────────────────────────╮
49
+ │ convert Convert Discogs XML dumps into flat files (Parquet or JSONL). │
50
+ │ load Load Discogs XML dumps into a database. │
51
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
52
+
53
+ ```
54
+
55
+ ### discogskit convert
56
+
57
+ Convert Discogs XML dumps into flat files.
58
+
59
+ | Option | Values |
60
+ |-----------------|-------------------------------|
61
+ | Output formats | `parquet`, `jsonl` |
62
+ | Compression (Parquet) | `zstd` (default), `snappy`, `gzip`, `none` |
63
+ | Compression (JSONL) | `gzip`, `bzip2`, `none` (default) |
64
+
65
+ <details>
66
+ <summary>Full command help</summary>
67
+
68
+ ```text
69
+
70
+ Usage: discogskit convert [OPTIONS] PATHS...
71
+
72
+ Convert Discogs XML dumps into flat files (Parquet or JSONL).
73
+
74
+ ╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
75
+ │ * paths PATHS... One or more .xml.gz files or directories containing them │
76
+ │ [required] │
77
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
78
+ ╭─ Options ────────────────────────────────────────────────────────────────────────────╮
79
+ │ --format -f TEXT Output format: parquet or jsonl │
80
+ │ [default: parquet] │
81
+ │ --output PATH Output directory [default: .] │
82
+ │ --compression TEXT Compression codec. Parquet: gzip, │
83
+ │ snappy, zstd (default), none. JSONL: │
84
+ │ bzip2, gzip, none (default). │
85
+ │ --parse-workers INTEGER Number of parallel parse workers │
86
+ │ [default: 4] │
87
+ │ --chunk-mb INTEGER Split XML into chunks of roughly │
88
+ │ this size (MB) │
89
+ │ [default: 256] │
90
+ │ --write-queue INTEGER Max chunks buffered in memory before │
91
+ │ writes must catch up │
92
+ │ [default: 2] │
93
+ │ --keep-xml --no-keep-xml Keep decompressed XML file after │
94
+ │ converting │
95
+ │ [default: no-keep-xml] │
96
+ │ --overwrite --no-overwrite Overwrite existing output files │
97
+ │ [default: no-overwrite] │
98
+ │ --profile --no-profile Print detailed per-table timing │
99
+ │ breakdown after convert │
100
+ │ [default: no-profile] │
101
+ │ --progress --no-progress Show a progress bar instead of │
102
+ │ per-chunk output │
103
+ │ [default: progress] │
104
+ │ --strict --no-strict Warn about unhandled XML elements │
105
+ │ during parsing │
106
+ │ [default: no-strict] │
107
+ │ --help Show this message and exit. │
108
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
109
+
110
+ ```
111
+
112
+ </details>
113
+
114
+ #### Examples
115
+
116
+ ```shell
117
+ # Convert releases to Parquet (default)
118
+ discogskit convert --format parquet discogs_20260301_releases.xml.gz
119
+
120
+ # Convert to JSONL with gzip compression
121
+ discogskit convert --format jsonl --compression gzip discogs_20260301_artists.xml.gz
122
+
123
+ # Convert all dump files in the current directory
124
+ discogskit convert --format parquet .
125
+
126
+ # Keep decompressed XML after converting
127
+ discogskit convert --format parquet --keep-xml discogs_20260301_releases.xml.gz
128
+ ```
129
+
130
+ ### discogskit load
131
+
132
+ Load Discogs XML dumps into a database.
133
+
134
+ | Database | Versions |
135
+ |----------|----------|
136
+ | SQLite | 3.x |
137
+ | PostgreSQL | 14+ |
138
+
139
+ <details>
140
+ <summary>Full command help</summary>
141
+
142
+ ```text
143
+
144
+ Usage: discogskit load [OPTIONS] PATHS...
145
+
146
+ Load Discogs XML dumps into a database.
147
+
148
+ ╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮
149
+ │ * paths PATHS... One or more .xml.gz files or directories containing them │
150
+ │ [required] │
151
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
152
+ ╭─ Options ────────────────────────────────────────────────────────────────────────────╮
153
+ │ --dsn TEXT Database DSN (e.g., │
154
+ │ postgresql://localhost/postgres) or │
155
+ │ path to SQLite file │
156
+ │ [env var: DATABASE_URL] │
157
+ │ [default: │
158
+ │ postgresql://localhost/discogskit] │
159
+ │ --parse-workers INTEGER Number of parallel parse workers │
160
+ │ [default: 4] │
161
+ │ --write-workers INTEGER Number of parallel database write │
162
+ │ workers │
163
+ │ [default: 1] │
164
+ │ --index-workers INTEGER Number of parallel index creation │
165
+ │ workers │
166
+ │ [default: 2] │
167
+ │ --chunk-mb INTEGER Split XML into chunks of roughly this │
168
+ │ size (MB) │
169
+ │ [default: 256] │
170
+ │ --write-queue INTEGER Max chunks buffered in memory before │
171
+ │ writes must catch up │
172
+ │ [default: 2] │
173
+ │ --keep-xml --no-keep-xml Keep decompressed XML file after │
174
+ │ loading │
175
+ │ [default: no-keep-xml] │
176
+ │ --overwrite --no-overwrite Overwrite existing tables in the │
177
+ │ database │
178
+ │ [default: no-overwrite] │
179
+ │ --profile --no-profile Print detailed per-table timing │
180
+ │ breakdown after load │
181
+ │ [default: no-profile] │
182
+ │ --progress --no-progress Show a progress bar instead of │
183
+ │ per-chunk output │
184
+ │ [default: progress] │
185
+ │ --strict --no-strict Warn about unhandled XML elements │
186
+ │ during parsing │
187
+ │ [default: no-strict] │
188
+ │ --help Show this message and exit. │
189
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
190
+ ╭─ PostgreSQL ─────────────────────────────────────────────────────────────────────────╮
191
+ │ --pg-unlogged --no-pg-unlogged Skip WAL for faster writes (tables stay │
192
+ │ unlogged; data lost on crash) │
193
+ │ [default: no-pg-unlogged] │
194
+ │ --pg-tune --no-pg-tune Temporarily apply settings optimized for bulk │
195
+ │ loading │
196
+ │ [default: no-pg-tune] │
197
+ │ --pg-fk --no-pg-fk Add foreign key constraints after load │
198
+ │ [default: no-pg-fk] │
199
+ ╰──────────────────────────────────────────────────────────────────────────────────────╯
200
+
201
+ ```
202
+
203
+ </details>
204
+
205
+ #### Examples
206
+
207
+ ```shell
208
+ # Load releases into PostgreSQL (default DSN: postgresql://localhost/discogskit)
209
+ discogskit load discogs_20260301_releases.xml.gz
210
+
211
+ # Load into a specific PostgreSQL database
212
+ discogskit load --dsn "postgresql://user:pass@localhost/discogs" discogs_20260301_releases.xml.gz
213
+
214
+ # Load into SQLite
215
+ discogskit load --dsn discogs.db discogs_20260301_releases.xml.gz
216
+
217
+ # Load all dump files from a directory
218
+ discogskit load --dsn discogs.db .
219
+
220
+ # Use UNLOGGED tables for faster PostgreSQL writes (~2x speedup)
221
+ discogskit load --pg-unlogged discogs_20260301_releases.xml.gz
222
+
223
+ # Temporarily tune PostgreSQL for bulk loading
224
+ discogskit load --pg-tune discogs_20260301_releases.xml.gz
225
+
226
+ # Add foreign key constraints after load
227
+ discogskit load --pg-fk discogs_20260301_releases.xml.gz
228
+
229
+ # Use multiple write workers for parallel database inserts
230
+ discogskit load --write-workers 4 discogs_20260301_releases.xml.gz
231
+ ```
232
+
233
+ ## Benchmarks
234
+
235
+ Full load of the `20260301` data dump (artists, labels, masters, releases) into PostgreSQL 18
236
+ on a 24 GB Apple MacBook Air M3.
237
+
238
+ | | discogs-xml2db Python | discogs-xml2db .NET | discogskit | discogskit `--pg-unlogged` |
239
+ |---|---:|---:|---:|---:|
240
+ | Parse + load | 0:59:11 | 1:01:08 | 18:55 | 9:25 |
241
+ | Indexes | 0:43:24 | 0:43:24 | 14:53 | 1:56 |
242
+ | **Total** | **1:42:35** | **1:44:32** | **33:49** | **11:22** |
243
+ | **Speedup** | **baseline** | **0.98x** | **3.0x** | **9.0x** |
244
+
245
+ <details>
246
+ <summary>Commands and detailed output</summary>
247
+
248
+ **discogs-xml2db Python**
249
+
250
+ ```shell
251
+ python3 run.py --apicounts --export artist --export label --export master --export release --output ./csv-dir [path]
252
+ python3 postgresql/psql.py < postgresql/sql/CreateTables.sql
253
+ python3 postgresql/importcsv.py ./csv-dir/*
254
+ python3 postgresql/psql.py < postgresql/sql/CreatePrimaryKeys.sql
255
+ python3 postgresql/psql.py < postgresql/sql/CreateFKConstraints.sql
256
+ python3 postgresql/psql.py < postgresql/sql/CreateIndexes.sql
257
+ ```
258
+
259
+ | Step | Time |
260
+ |---|---:|
261
+ | Export to CSV | 0:41:14 |
262
+ | Table creation | 0:00:01 |
263
+ | Data import | 0:17:56 |
264
+ | Primary keys | 0:19:21 |
265
+ | Foreign keys | 0:02:12 |
266
+ | Indexes | 0:21:51 |
267
+ | **Total** | **1:42:35** |
268
+
269
+ **discogs-xml2db .NET**
270
+
271
+ ```shell
272
+ discogs [paths]
273
+ python3 postgresql/psql.py < postgresql/sql/CreateTables.sql
274
+ python3 postgresql/importcsv.py ./csv-dir/*
275
+ python3 postgresql/psql.py < postgresql/sql/CreatePrimaryKeys.sql
276
+ python3 postgresql/psql.py < postgresql/sql/CreateFKConstraints.sql
277
+ python3 postgresql/psql.py < postgresql/sql/CreateIndexes.sql
278
+ ```
279
+
280
+ | Step | Time |
281
+ |---|---:|
282
+ | Export to CSV | 0:43:11 |
283
+ | Table creation | 0:00:01 |
284
+ | Data import | 0:17:56 |
285
+ | Primary keys | 0:19:21 |
286
+ | Foreign keys | 0:02:12 |
287
+ | Indexes | 0:21:51 |
288
+ | **Total** | **1:44:32** |
289
+
290
+ **discogskit**
291
+
292
+ ```shell
293
+ discogskit load --dsn postgresql://localhost:5432/discogskit --chunk-mb 256 \
294
+ --parse-workers 6 --write-workers 3 --index-workers 6 [path]
295
+ ```
296
+
297
+ | Entity | Records | Parse + load | Indexes | Total |
298
+ |---|---:|---:|---:|---:|
299
+ | Artists | 9,957,079 | 40.40s | 14.77s | 55.15s |
300
+ | Labels | 2,349,729 | 9.27s | 0.67s | 9.95s |
301
+ | Masters | 2,530,697 | 34.45s | 19.94s | 54.36s |
302
+ | Releases | 18,952,204 | 1,051.37s | 857.89s | 1,909.24s |
303
+ | **Total** | **33,789,709** | **1,135.49s** | **893.27s** | **2,028.70s** |
304
+
305
+ **discogskit `--pg-unlogged`**
306
+
307
+ ```shell
308
+ discogskit load --dsn postgresql://localhost:5432/discogskit --chunk-mb 256 --pg-unlogged \
309
+ --parse-workers 6 --write-workers 3 --index-workers 6 [path]
310
+ ```
311
+
312
+ | Entity | Records | Parse + load | Indexes | Total |
313
+ |---|---:|---:|---:|---:|
314
+ | Artists | 9,957,079 | 27.73s | 2.52s | 30.22s |
315
+ | Labels | 2,349,729 | 8.84s | 0.51s | 9.34s |
316
+ | Masters | 2,530,697 | 23.38s | 1.93s | 25.29s |
317
+ | Releases | 18,952,204 | 505.45s | 111.46s | 616.86s |
318
+ | **Total** | **33,789,709** | **565.40s** | **116.42s** | **681.71s** |
319
+
320
+ </details>
321
+
322
+
323
+ ## License
324
+
325
+ discogskit is licensed under the [Apache License 2.0](LICENSE.txt).