Source code for operators.iceberg.maintain_iceberg_table
from airflow.utils.decorators import apply_defaults
from typing import Dict, Optional, Any
from operators.iceberg.base_iceberg_table import BaseIcebergTableOperator
[docs]
class MaintainIcebergTableOperator(BaseIcebergTableOperator):
"""
Performs maintenance operations on Iceberg tables in AWS Athena.
This operator combines small files, cleans up old snapshots, and provides
before/after statistics on the table.
:param table_name: The name of the Iceberg table to maintain
:type table_name: str
:param database: The database containing the Iceberg table
:type database: str
:param optimize: Whether to run OPTIMIZE to combine small files
:type optimize: bool
:param vacuum: Whether to run VACUUM to clean up old snapshots
:type vacuum: bool
:param optimize_options: Additional options for the OPTIMIZE command
:type optimize_options: str
"""
template_fields = BaseIcebergTableOperator.template_fields + ["optimize_options"]
ui_color = "#D6EAF8" # Light Blue shade
@apply_defaults
def __init__(
self,
table_name: str,
database: Optional[str] = None,
optimize: bool = True,
vacuum: bool = True,
optimize_options: str = "REWRITE DATA USING BIN_PACK",
*args,
**kwargs,
):
super().__init__(table_name=table_name, database=database, *args, **kwargs)
self.optimize = optimize
self.vacuum = vacuum
self.optimize_options = optimize_options
[docs]
def execute(self, context: Dict) -> Dict[str, Any]:
"""
Execute the maintenance operations on the Iceberg table.
:param context: Airflow context
:type context: Dict
"""
# Get statistics before maintenance
stats_before = self._get_table_statistics()
self.log.info(f"BEFORE MAINTENANCE, table statistics: {stats_before}")
# Run optimize if requested
if self.optimize:
optimize_query = (
f"OPTIMIZE {self.database}.{self.table_name} {self.optimize_options}"
)
self._execute_athena_query(sql=optimize_query)
# Run vacuum if requested
if self.vacuum:
vacuum_query = f"VACUUM {self.database}.{self.table_name}"
self._execute_athena_query(sql=vacuum_query)
# Get statistics after maintenance
stats_after = self._get_table_statistics()
self.log.info(f"AFTER MAINTENANCE, table statistics: {stats_after}")
# Calculate improvements
file_count_reduction = 0
size_reduction_mb = 0
if isinstance(stats_before["file_count"], (int, float)) and isinstance(
stats_after["file_count"], (int, float)
):
file_count_reduction = (
stats_before["file_count"] - stats_after["file_count"]
)
if isinstance(stats_before["total_size_mb"], (int, float)) and isinstance(
stats_after["total_size_mb"], (int, float)
):
size_reduction_mb = (
stats_before["total_size_mb"] - stats_after["total_size_mb"]
)
# Log a summary of the operation
self.log.info(
f"✅ Maintenance completed for {self.database}.{self.table_name}. "
f"Files: {stats_before['file_count']} → {stats_after['file_count']} "
f"({file_count_reduction:+.0f}), "
f"Size: {stats_before['total_size_mb']:.2f}MB → {stats_after['total_size_mb']:.2f}MB "
f"({size_reduction_mb:+.2f}MB)"
)