Skip to content

Commit 22e8b42

Browse files
[wip] define method to merge append deltas into metadata db file
1 parent 8cb28ad commit 22e8b42

5 files changed

Lines changed: 114 additions & 0 deletions

File tree

metadata.duckdb

32.8 MB
Binary file not shown.

tests/test_metadata.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,24 @@ def test_tdm_current_records_most_recent_version(timdex_metadata_with_deltas):
262262
== most_recent.iloc[0]["run_timestamp"]
263263
)
264264
assert current_version.iloc[0]["run_id"] == most_recent.iloc[0]["run_id"]
265+
266+
267+
def test_tdm_compact_append_deltas(timdex_metadata_with_deltas):
268+
# get record count from static db
269+
metadata_db_current_count = timdex_metadata_with_deltas.conn.query(
270+
"""select count(*) as count from static_db.records;"""
271+
).fetchone()[0]
272+
total_count = timdex_metadata_with_deltas.records_count
273+
274+
# compact append deltas into static db file
275+
timdex_metadata_with_deltas.compact_append_deltas()
276+
timdex_metadata_with_deltas.refresh()
277+
278+
# get updated record count from static db
279+
metadata_db_updated_count = timdex_metadata_with_deltas.conn.query(
280+
"""select count(*) as count from static_db.records;"""
281+
).fetchone()[0]
282+
283+
# verify the addition of new records
284+
assert metadata_db_current_count < metadata_db_updated_count
285+
assert metadata_db_updated_count == total_count

tests/test_s3client.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,22 @@ def test_split_s3_uri_invalid():
4242
client._split_s3_uri("timdex/path/to/file.txt")
4343

4444

45+
def test_list_objects(s3_bucket_mocked, tmp_path):
46+
client = S3Client()
47+
48+
# Create a test file
49+
test_file = tmp_path / "test.txt"
50+
test_file.write_text("test content")
51+
52+
# Upload the file
53+
s3_uri = "s3://timdex/metadata/append_deltas/test.txt"
54+
client.upload_file(test_file, s3_uri)
55+
56+
# Verify list of objects
57+
s3_prefix = "s3://timdex/metadata/append_deltas"
58+
assert client.list_objects(s3_prefix) == ["metadata/append_deltas/test.txt"]
59+
60+
4561
def test_upload_download_file(s3_bucket_mocked, tmp_path):
4662
"""Test upload_file and download_file methods."""
4763
client = S3Client()

timdex_dataset_api/metadata.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,77 @@ def _create_current_records_view(self, conn: DuckDBPyConnection) -> None:
442442
"""
443443
conn.execute(query)
444444

445+
def merge_append_deltas(self) -> None:
446+
"""Merge append deltas into the static metadata database file."""
447+
start_time = time.perf_counter()
448+
449+
s3_client = S3Client()
450+
451+
# get filenames of append deltas
452+
append_delta_filenames = (
453+
self.conn.query(
454+
"""
455+
select distinct(filename)
456+
from metadata.append_deltas
457+
"""
458+
)
459+
.to_df()["filename"]
460+
.to_list()
461+
)
462+
463+
if len(append_delta_filenames) == 0:
464+
logger.info("No append deltas found.")
465+
return
466+
467+
logger.info(
468+
f"Found {len(append_delta_filenames)} append delta file(s) in {self.append_deltas_path}: {append_delta_filenames}"
469+
)
470+
471+
with tempfile.TemporaryDirectory() as temp_dir:
472+
# create local copy of the static metadata database (static db) file
473+
local_db_path = str(Path(temp_dir) / self.metadata_database_filename)
474+
if self.location_scheme == "s3":
475+
s3_client.download_file(
476+
s3_uri=self.metadata_database_path, local_path=local_db_path
477+
)
478+
else:
479+
shutil.copy(self.metadata_database_path, local_db_path)
480+
481+
# attach to local static db
482+
self.conn.execute(f"""attach '{local_db_path}' AS local_static_db;""")
483+
484+
# insert records from append deltas to local static db
485+
self.conn.execute(
486+
"""
487+
insert into local_static_db.records
488+
select *
489+
from metadata.append_deltas
490+
"""
491+
)
492+
493+
# detach from local static db
494+
self.conn.execute("""detach local_static_db;""")
495+
496+
# overwrite static db file with local version
497+
if self.location_scheme == "s3":
498+
s3_client.upload_file(
499+
local_path=local_db_path,
500+
s3_uri=self.metadata_database_path,
501+
)
502+
else:
503+
shutil.copy(src=local_db_path, dst=self.metadata_database_path)
504+
505+
# delete append deltas
506+
for append_delta in append_delta_filenames:
507+
if self.location_scheme == "s3":
508+
s3_client.delete_file(s3_uri=append_delta)
509+
else:
510+
shutil.copy(self.metadata_database_path, local_db_path)
511+
512+
logger.info(
513+
f"Merged append deltas into the metadata database file: {self.metadata_database_path}, {time.perf_counter()-start_time}s"
514+
)
515+
445516
def write_append_delta_duckdb(self, filepath: str) -> None:
446517
"""Write an append delta for an ETL parquet file.
447518

timdex_dataset_api/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ def object_exists(self, s3_uri: str) -> bool:
5858
return False
5959
raise
6060

61+
def list_objects(self, s3_prefix: str) -> list[str]:
62+
bucket, _ = self._split_s3_uri(s3_prefix)
63+
objects = [obj.key for obj in self.resource.Bucket(bucket).objects.all()]
64+
logger.debug(f"Found {len(objects)} objects in {s3_prefix}: {objects}")
65+
return objects
66+
6167
def download_file(self, s3_uri: str, local_path: str | pathlib.Path) -> None:
6268
bucket, key = self._split_s3_uri(s3_uri)
6369
local_path = pathlib.Path(local_path)

0 commit comments

Comments
 (0)