## ----include = FALSE----------------------------------------------------------
# Evaluate chunks only where the ducklake DuckDB extension is already
# installed. The probe never downloads anything, so building this vignette
# needs no network access.
ducklake_available <- ducklake::ducklake_extension_available()
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = ducklake_available
)
# Use a unique temp directory for this vignette to avoid conflicts during R CMD check
vignette_temp_dir <- file.path(tempdir(), "storage_backups_vignette")
dir.create(vignette_temp_dir, showWarnings = FALSE, recursive = TRUE)
knitr::opts_knit$set(root.dir = vignette_temp_dir)

## ----setup, message=FALSE-----------------------------------------------------
library(ducklake)
library(dplyr)
library(fs)

## ----storage-patterns, eval=FALSE---------------------------------------------
# # Local storage - fastest, but not shared
# attach_ducklake(
#   ducklake_name = "local_lake",
#   lake_path = "~/data/my_ducklake"
# )
# 
# # PostgreSQL catalog with S3 data - multi-client, scalable
# attach_ducklake(
#   ducklake_name = "shared_lake",
#   backend = "postgres",
#   catalog_connection_string = "dbname=ducklake_catalog host=localhost",
#   lake_path = "s3://my-bucket/ducklake/data"
# )
# 
# # SQLite catalog - lightweight multi-client option
# attach_ducklake(
#   ducklake_name = "team_lake",
#   backend = "sqlite",
#   catalog_connection_string = "~/data/metadata.sqlite",
#   lake_path = "~/data/parquet_files"
# )

## ----storage-secrets, eval=FALSE----------------------------------------------
# # Explicit keys, scoped to one bucket
# create_storage_secret(
#   "s3",
#   key_id = Sys.getenv("AWS_ACCESS_KEY_ID"),
#   secret = Sys.getenv("AWS_SECRET_ACCESS_KEY"),
#   region = "us-east-1",
#   scope = "s3://my-bucket"
# )
# 
# # Or let the AWS credential chain find them (env vars, profiles,
# # instance metadata) -- no keys in code
# create_storage_secret("s3", provider = "credential_chain")
# 
# # Then attach as usual
# attach_ducklake("shared_lake", lake_path = "s3://my-bucket/ducklake/data")

## ----create-sample-lake-------------------------------------------------------
# Create a temporary directory for our demo
lake_dir <- file.path(vignette_temp_dir, "storage_demo")
dir.create(lake_dir, showWarnings = FALSE, recursive = TRUE)

# Install ducklake extension
# The ducklake extension only needs installing once per machine:
# install_ducklake()

# Create and populate a DuckLake
attach_ducklake(
  ducklake_name = "demo_lake",
  lake_path = lake_dir
)

# Add some data with transactions
with_transaction(
  create_table(mtcars[1:15, ], "cars"),
  author = "Demo User",
  commit_message = "Initial load"
)

with_transaction(
  get_ducklake_table("cars") |>
    mutate(hp_per_cyl = hp / cyl) |>
    replace_table("cars"),
  author = "Demo User",
  commit_message = "Add hp_per_cyl metric"
)

with_transaction(
  get_ducklake_table("cars") |>
    mutate(mpg_adjusted = if_else(cyl == 4, mpg * 1.1, mpg)) |>
    replace_table("cars"),
  author = "Demo User",
  commit_message = "Add adjusted MPG for 4-cylinder cars"
)

## ----view-catalog-files-------------------------------------------------------
dir_tree(lake_dir)

## ----view-data-files----------------------------------------------------------
# Data files are organized by schema and table
main_dir <- file.path(lake_dir, "main")

dir_tree(main_dir, recurse = 2)
  
# Get details about parquet files
parquet_files <- dir_ls(main_dir, recurse = TRUE, regexp = "\\.parquet$")
for (f in parquet_files) {
  cat(sprintf("  %s (%s bytes)\n", 
              path_file(f), 
              file.size(f)))
}

## ----examine-structure--------------------------------------------------------
# List all snapshots to see the version history
snapshots <- list_table_snapshots("cars")
snapshots |>
  select(snapshot_id, author, commit_message)

## ----backup-catalog-simple----------------------------------------------------
# Create backup directory
backup_dir <- file.path(lake_dir, "backups")
dir.create(backup_dir, showWarnings = FALSE)

# Release file locks before copying the catalog
detach_ducklake("demo_lake", shutdown = TRUE)

# Copy the catalog file to create a backup
file.copy(
  from = file.path(lake_dir, "demo_lake.ducklake"),
  to = file.path(backup_dir, "demo_lake.ducklake")
)

# Copy the data directory as well
dir_copy(
  path = file.path(lake_dir, "main"),
  new_path = file.path(backup_dir, "main")
)

# Verify the backup was created
dir_tree(backup_dir)

# To work with the backup, attach it. override_data_path is needed because
# the catalog remembers the original data location, which the backup no
# longer matches.
attach_ducklake(
  ducklake_name = "demo_lake",
  lake_path = backup_dir,
  override_data_path = TRUE
)

# Verify you're working with the backup
list_table_snapshots("cars")

# You can switch back to the original by detaching and reattaching
detach_ducklake("demo_lake")
attach_ducklake("demo_lake", lake_path = lake_dir)

## ----backup-storage-local, eval=FALSE-----------------------------------------
# # Use file system tools to copy the entire data directory
# backup_data_dir <- file.path(lake_dir, "backups", "main_backup")
# dir_copy(
#   path = file.path(lake_dir, "main"),
#   new_path = backup_data_dir
# )

## ----backup-cloud, eval=FALSE-------------------------------------------------
# # Original
# attach_ducklake(
#   ducklake_name = "prod_lake",
#   lake_path = "s3://original-bucket/data"
# )
# 
# # After recovery from replicated bucket
# attach_ducklake(
#   ducklake_name = "prod_lake",
#   lake_path = "s3://backup-bucket/data",
#   override_data_path = TRUE
# )

## ----recovery-catalog, eval=FALSE---------------------------------------------
# # Restore from backup by copying the backup file
# # (backup_dir here is a directory created earlier, e.g. by backup_ducklake())
# file.copy(
#   from = file.path(backup_dir, "demo_lake.ducklake"),
#   to = file.path(lake_dir, "demo_lake.ducklake"),
#   overwrite = TRUE
# )
# 
# # Reattach to the restored database
# attach_ducklake("demo_lake", lake_path = lake_dir)
# 
# # Verify recovery by listing snapshots
# list_table_snapshots("cars")

## ----recovery-data, eval=FALSE------------------------------------------------
# # Restore data files from backup
# dir_copy(
#   path = backup_data_dir,
#   new_path = file.path(lake_dir, "main"),
#   overwrite = TRUE
# )
# 
# # DuckLake will automatically reconnect to the restored files
# # since the catalog maintains the file paths

## ----routine-maintenance, eval=FALSE------------------------------------------
# # Compact small adjacent Parquet files into larger ones
# merge_adjacent_files()
# 
# # Preview a retention policy, then apply it
# expire_snapshots(older_than = Sys.time() - 30 * 24 * 60 * 60, dry_run = TRUE)
# expire_snapshots(older_than = Sys.time() - 30 * 24 * 60 * 60)
# 
# # Expired snapshots only *schedule* file deletion; this reclaims the storage
# cleanup_old_files(cleanup_all = TRUE)
# 
# # Rewrite data files whose rows have mostly been deleted
# rewrite_data_files(delete_threshold = 0.5)
# 
# # Remove untracked files from the data path -- always dry-run this one first
# delete_orphaned_files(dry_run = TRUE, cleanup_all = TRUE)

## ----maintenance-backup-sequence, eval=FALSE----------------------------------
# # Recommended backup sequence
# 
# # 1. Run maintenance operations (if needed)
# merge_adjacent_files()
# expire_snapshots(older_than = Sys.time() - 30 * 24 * 60 * 60)
# cleanup_old_files(cleanup_all = TRUE)
# 
# # 2. Ensure all transactions are committed
# # (no pending work)
# 
# # 3. Release file locks before copying the catalog
# detach_ducklake("demo_lake", shutdown = TRUE)
# 
# # 4. Back up catalog
# dir.create(file.path(lake_dir, "backups"), showWarnings = FALSE)
# file.copy(
#   from = file.path(lake_dir, "demo_lake.ducklake"),
#   to = file.path(lake_dir, "backups",
#                  paste0("backup_", format(Sys.time(), "%Y%m%d_%H%M%S"), ".ducklake"))
# )
# 
# # 5. Back up data files
# dir_copy(
#   path = file.path(lake_dir, "main"),
#   new_path = file.path(lake_dir, "backups", "main_latest")
# )
# 
# # 6. Re-attach and continue working
# attach_ducklake("demo_lake", lake_path = lake_dir)

## ----complete-backup-function-------------------------------------------------
# Create a complete backup with timestamp
backup_dir <- backup_ducklake(
  ducklake_name = "demo_lake",
  lake_path = lake_dir,
  backup_path = file.path(lake_dir, "backups")
)

# The function returns the backup directory path
print(backup_dir)

## ----cleanup------------------------------------------------------------------
# Detach the demo lake
detach_ducklake("demo_lake")

# Clean up temporary files
unlink(lake_dir, recursive = TRUE)

