quickbase-extract 0.2.0__tar.gz → 0.3.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.
Files changed (30) hide show
  1. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/.editorconfig +0 -8
  2. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/.gitignore +0 -9
  3. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/CHANGELOG.md +34 -0
  4. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/PKG-INFO +193 -57
  5. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/README.md +192 -56
  6. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/pyproject.toml +1 -1
  7. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/src/quickbase_extract/__init__.py +12 -2
  8. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/src/quickbase_extract/api_handlers.py +5 -3
  9. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/src/quickbase_extract/cache_manager.py +6 -0
  10. quickbase_extract-0.3.0/src/quickbase_extract/cache_sync.py +191 -0
  11. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/tests/conftest.py +19 -0
  12. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/tests/test_api_handlers.py +22 -3
  13. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/tests/test_cache_manager.py +127 -32
  14. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/tests/test_cache_orchestration.py +2 -0
  15. quickbase_extract-0.3.0/tests/test_cache_sync.py +360 -0
  16. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/tests/test_report_data.py +13 -2
  17. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/tests/test_report_metadata.py +2 -0
  18. quickbase_extract-0.2.0/src/quickbase_extract/cache_sync.py +0 -94
  19. quickbase_extract-0.2.0/tests/test_cache_sync.py +0 -117
  20. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/.pre-commit-config.yaml +0 -0
  21. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/.python-version +0 -0
  22. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/LICENSE.txt +0 -0
  23. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/TODO.md +0 -0
  24. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/src/quickbase_extract/cache_orchestration.py +0 -0
  25. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/src/quickbase_extract/config.py +0 -0
  26. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/src/quickbase_extract/py.typed +0 -0
  27. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/src/quickbase_extract/report_data.py +0 -0
  28. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/src/quickbase_extract/report_metadata.py +0 -0
  29. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/src/quickbase_extract/utils.py +0 -0
  30. {quickbase_extract-0.2.0 → quickbase_extract-0.3.0}/tests/test_utils.py +0 -0
@@ -20,14 +20,6 @@ indent_style = space
20
20
  indent_size = 4
21
21
  max_line_length = 120
22
22
 
23
- # ============================================================
24
- # JavaScript / TypeScript
25
- # ============================================================
26
- [*.{js,jsx,ts,tsx}]
27
- indent_style = space
28
- indent_size = 2
29
- max_line_length = 120
30
-
31
23
  # ============================================================
32
24
  # JSON
33
25
  # ============================================================
@@ -48,15 +48,6 @@ src/*.egg-info/
48
48
  *.log
49
49
  logs/
50
50
 
51
- # Database files
52
- *.db
53
- *.sqlite
54
- *.sqlite3
55
-
56
- # Node modules
57
- node_modules/
58
- npm-debug.log
59
-
60
51
  # IDE caches
61
52
  .idea/
62
53
  *.swp
@@ -5,6 +5,40 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.0] - 2026-04-27
9
+
10
+ ### Added
11
+
12
+ - `complete_cache_refresh()` function in `cache_sync` module for selective cache refresh in development
13
+ - Support for granular cache refresh control:
14
+ - `force_all=True`: Refresh both metadata and data caches
15
+ - `force_metadata=True`: Refresh only metadata cache
16
+ - `force_data=True`: Refresh only data cache
17
+ - Cache refresh workflow: clear /tmp → fetch fresh from Quickbase → update S3 → re-sync to /tmp
18
+ - Manual development toggles in Lambda handlers for testing cache refresh without API changes
19
+ - Comprehensive test suite for complete cache refresh functionality
20
+
21
+ ### Changed
22
+
23
+ - Lambda workers can now force complete cache refresh for development/debugging by toggling code variables
24
+ - Cache refresh strategy clarified: selective refresh based on what changed (metadata vs data)
25
+
26
+ ### Fixed
27
+
28
+ - Dev workflow now supports forcing cache refresh without modifying Lambda event API
29
+ - Ensures /tmp and S3 are synchronized after cache refresh operations
30
+
31
+ ## [0.2.1] - 2026-04-25
32
+
33
+ ### Fixed
34
+
35
+ - `sync_from_s3()` now preserves S3 `LastModified` timestamps via `os.utime()`, so `get_cache_age_hours()` returns accurate ages after S3 restore (previously always returned ~0 on Lambda cold start)
36
+ - Renamed misleading `should_sync` variable to `already_synced` in `sync_from_s3_once()` for clarity
37
+
38
+ ### Removed
39
+
40
+ - `FORCE_CACHE_REFRESH` environment variable support from `sync_from_s3_once()` — use `force=True` parameter instead
41
+
8
42
  ## [0.2.0] - 2026-04-22
9
43
 
10
44
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quickbase-extract
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Extract and cache Quickbase report data with built-in error handling and S3 support
5
5
  Project-URL: Homepage, https://github.com/tbrezler/quickbase-extract
6
6
  Project-URL: Repository, https://github.com/tbrezler/quickbase-extract.git
@@ -882,7 +882,6 @@ s3://mit-bio-quickbase/my_project/dev/cache/report_metadata/...
882
882
  | `CACHE_BUCKET` | S3 bucket for Lambda cache | - |
883
883
  | `METADATA_STALE_HOURS` | Threshold (hours) for metadata cache staleness | `168` (7 days) |
884
884
  | `DATA_STALE_HOURS` | Threshold (hours) for data cache staleness | `24` (1 day) |
885
- | `FORCE_CACHE_REFRESH` | If set to "true", forces cache refresh on next sync | - |
886
885
 
887
886
  ### Custom Cache Location
888
887
 
@@ -956,10 +955,30 @@ def lambda_handler(event, context):
956
955
  - Cold start: Syncs cache from S3
957
956
  - Cache freshness: Auto-refreshes if stale
958
957
  - Data fetching: Queries Quickbase and caches results
958
+ - Development: Supports forcing complete cache refresh for testing
959
959
  """
960
960
  client = get_client()
961
961
  cache_mgr = get_cache_manager()
962
962
 
963
+ # OPTIONAL: Set to True to force cache refresh (for development/debugging only)
964
+ # Only one should be True at a time. force_all overrides the others.
965
+ FORCE_COMPLETE_CACHE_REFRESH_ALL = False
966
+ FORCE_COMPLETE_CACHE_REFRESH_METADATA = False
967
+ FORCE_COMPLETE_CACHE_REFRESH_DATA = False
968
+
969
+ # Force complete cache refresh if needed (dev/debugging only)
970
+ if (FORCE_COMPLETE_CACHE_REFRESH_ALL or FORCE_COMPLETE_CACHE_REFRESH_METADATA
971
+ or FORCE_COMPLETE_CACHE_REFRESH_DATA):
972
+ from quickbase_extract import complete_cache_refresh
973
+ complete_cache_refresh(
974
+ cache_manager=cache_mgr,
975
+ client=client,
976
+ report_configs=get_all_reports(),
977
+ force_all=FORCE_COMPLETE_CACHE_REFRESH_ALL,
978
+ force_metadata=FORCE_COMPLETE_CACHE_REFRESH_METADATA,
979
+ force_data=FORCE_COMPLETE_CACHE_REFRESH_DATA,
980
+ )
981
+
963
982
  # Step 1: Sync cache from S3 on cold start (only on first invocation)
964
983
  sync_from_s3_once(cache_mgr)
965
984
 
@@ -1265,7 +1284,6 @@ Checks metadata and data caches independently. Refreshes only the caches that ar
1265
1284
  **Environment Variables:**
1266
1285
  - `METADATA_STALE_HOURS`: Override default metadata staleness threshold (in hours)
1267
1286
  - `DATA_STALE_HOURS`: Override default data staleness threshold (in hours)
1268
- - `FORCE_ALL_CACHE_REFRESH`: Set to "true" to force refresh regardless of cache state
1269
1287
 
1270
1288
  **Returns:** None
1271
1289
 
@@ -1565,6 +1583,48 @@ def lambda_handler(event, context):
1565
1583
  metadata = load_report_metadata_batch(cache_mgr, config)
1566
1584
  ```
1567
1585
 
1586
+ #### `complete_cache_refresh(cache_manager, client, report_configs, force_all=False, force_metadata=False, force_data=False)`
1587
+
1588
+ Completely refresh cache for development/debugging: clear /tmp, fetch fresh from Quickbase, update S3, re-sync to /tmp.
1589
+
1590
+ This is a development utility for forcing a complete cache refresh when report metadata or configurations change. Clears local /tmp cache, fetches fresh data from Quickbase, writes to S3, and re-syncs to /tmp.
1591
+
1592
+ **Parameters:**
1593
+ - `cache_manager` (CacheManager): Cache manager instance
1594
+ - `client`: Quickbase API client for fetching fresh data
1595
+ - `report_configs` (list[ReportConfig]): List of all ReportConfig instances to refresh
1596
+ - `force_all` (bool): If True, refresh both metadata and data. Defaults to False.
1597
+ - `force_metadata` (bool): If True (and `force_all` is False), refresh only metadata. Defaults to False.
1598
+ - `force_data` (bool): If True (and `force_all` is False), refresh only data. Defaults to False.
1599
+
1600
+ **Returns:** None
1601
+
1602
+ **Raises:**
1603
+ - `Exception`: If cache clearing or refresh operations fail
1604
+
1605
+ **Example:**
1606
+ ```python
1607
+ from quickbase_extract import complete_cache_refresh
1608
+
1609
+ # Refresh only metadata (after changing report configurations)
1610
+ complete_cache_refresh(
1611
+ cache_manager=cache_mgr,
1612
+ client=qb_client,
1613
+ report_configs=get_all_reports(),
1614
+ force_metadata=True
1615
+ )
1616
+
1617
+ # Refresh all (metadata + data)
1618
+ complete_cache_refresh(
1619
+ cache_manager=cache_mgr,
1620
+ client=qb_client,
1621
+ report_configs=get_all_reports(),
1622
+ force_all=True
1623
+ )
1624
+ ```
1625
+
1626
+ **Note:** This function is designed for development/debugging. To use in Lambda, add toggles to your handler (see "Development/Debug Mode" section below).
1627
+
1568
1628
  ### Query Execution with Retry Logic
1569
1629
 
1570
1630
  #### `handle_query(client, table_id, *, select=None, where=None, sort_by=None, group_by=None, options=None, description="", max_retries=3)`
@@ -2108,43 +2168,6 @@ fields = info["fields"]
2108
2168
 
2109
2169
  ---
2110
2170
 
2111
- ### Issue: "FORCE_CACHE_REFRESH not working"
2112
-
2113
- **Symptom:** Set `FORCE_CACHE_REFRESH=true` but cache still not refreshing.
2114
-
2115
- **Cause:** Environment variable case-sensitive or not set correctly.
2116
-
2117
- **Solution:**
2118
-
2119
- 1. **Verify env var is set (case-sensitive):**
2120
- ```bash
2121
- # Must be exactly this (uppercase)
2122
- export FORCE_ALL_CACHE_REFRESH=true
2123
-
2124
- # Not these (wrong):
2125
- export force_cache_refresh=true
2126
- export Force_Cache_Refresh=true
2127
- ```
2128
-
2129
- 2. **Verify it's being read:**
2130
- ```python
2131
- import os
2132
- print(os.environ.get("FORCE_ALL_CACHE_REFRESH")) # Should print "true"
2133
- ```
2134
-
2135
- 3. **Use programmatic force instead:**
2136
- ```python
2137
- ensure_cache_freshness(
2138
- client=client,
2139
- cache_manager=cache_mgr,
2140
- report_configs_all=get_all_reports(),
2141
- report_configs_to_cache=get_reports_to_cache(),
2142
- force_all=True # Programmatic force (always works)
2143
- )
2144
- ```
2145
-
2146
- ---
2147
-
2148
2171
  ### Issue: Data fetch returns empty or different results
2149
2172
 
2150
2173
  **Symptom:** `get_data()` returns empty list or fewer records than expected.
@@ -2282,6 +2305,47 @@ ask_values = {"ask1": "value"} # No underscores
2282
2305
 
2283
2306
  ---
2284
2307
 
2308
+ ### Issue: Lambda has old cached data after I changed report metadata
2309
+
2310
+ **Symptom:** You updated a Quickbase report's fields, filters, or configuration, but your Lambda returns stale data.
2311
+
2312
+ **Cause:** Cache was loaded before your changes, and it hasn't become "stale" enough to auto-refresh yet.
2313
+
2314
+ **Solutions:**
2315
+
2316
+ 1. **Quick fix: Use force refresh toggle (development only)**
2317
+
2318
+ In your Lambda handler, temporarily set:
2319
+ ```python
2320
+ FORCE_COMPLETE_CACHE_REFRESH_METADATA = True
2321
+ ```
2322
+
2323
+ Upload new build, invoke Lambda, check logs for refresh. Then revert flag to `False`.
2324
+
2325
+ 2. **For immediate production fix:**
2326
+
2327
+ Manually delete files from S3:
2328
+ ```bash
2329
+ aws s3 rm s3://my-quickbase-cache-bucket/prod/cache/report_metadata/ --recursive
2330
+ ```
2331
+
2332
+ Next Lambda cold start will re-fetch fresh metadata.
2333
+
2334
+ 3. **To prevent in future:**
2335
+
2336
+ Reduce `metadata_stale_hours` threshold:
2337
+ ```python
2338
+ ensure_cache_freshness(
2339
+ client=client,
2340
+ cache_manager=cache_mgr,
2341
+ report_configs_all=get_all_reports(),
2342
+ report_configs_to_cache=get_reports_to_cache(),
2343
+ metadata_stale_hours=24 # Check daily instead of 30 days
2344
+ )
2345
+ ```
2346
+
2347
+ ---
2348
+
2285
2349
  ### Issue: Performance degradation over time
2286
2350
 
2287
2351
  **Symptom:** First request fast, subsequent requests slow.
@@ -2453,23 +2517,6 @@ ensure_cache_freshness(
2453
2517
  )
2454
2518
  ```
2455
2519
 
2456
- #### Environment Variable Force
2457
-
2458
- Set `FORCE_ALL_CACHE_REFRESH=true` before invoking:
2459
-
2460
- ```bash
2461
- # In Lambda environment variables or shell
2462
- export FORCE_ALL_CACHE_REFRESH=true
2463
-
2464
- # Then call normally (will force refresh automatically)
2465
- ensure_cache_freshness(
2466
- client=client,
2467
- cache_manager=cache_mgr,
2468
- report_configs_all=get_all_reports(),
2469
- report_configs_to_cache=get_reports_to_cache()
2470
- )
2471
- ```
2472
-
2473
2520
  ### Cache-All-Data Mode
2474
2521
 
2475
2522
  For production, cache data for all reports instead of a subset:
@@ -2678,6 +2725,95 @@ def scheduled_cache_refresh(event, context):
2678
2725
  return {"statusCode": 200, "message": "Cache refreshed"}
2679
2726
  ```
2680
2727
 
2728
+ ### Development/Debug Mode: Forcing Complete Cache Refresh
2729
+
2730
+ When you modify report metadata or configurations in Quickbase, your Lambda may still use stale cached data. Use the force refresh toggles to clear everything and fetch fresh data.
2731
+
2732
+ #### When to Use
2733
+
2734
+ - You changed a report's filters or fields in Quickbase
2735
+ - You added/removed fields from a report
2736
+ - You renamed a report or table
2737
+ - You need to verify fresh data is being fetched
2738
+
2739
+ #### How to Use
2740
+
2741
+ 1. Open your Lambda handler code
2742
+ 2. Set one of the toggle flags to `True`:
2743
+
2744
+ ```python
2745
+ # In lambda_handler, find these lines:
2746
+ FORCE_COMPLETE_CACHE_REFRESH_ALL = False
2747
+ FORCE_COMPLETE_CACHE_REFRESH_METADATA = False
2748
+ FORCE_COMPLETE_CACHE_REFRESH_DATA = False
2749
+
2750
+ # Change to (example: refresh only metadata):
2751
+ FORCE_COMPLETE_CACHE_REFRESH_METADATA = True
2752
+ ```
2753
+
2754
+ 3. Upload new Lambda build
2755
+ 4. Invoke your Lambda (via API or CloudWatch event)
2756
+ 5. Check CloudWatch logs for cache refresh messages
2757
+ 6. **Revert the flag back to `False`** for normal operation
2758
+
2759
+ #### Flag Options
2760
+
2761
+ | Flag | What Gets Refreshed | Use When |
2762
+ |------|---------------------|----------|
2763
+ | `force_all=True` | Both metadata + data | Complete cache overhaul needed |
2764
+ | `force_metadata=True` | Only metadata | Report configuration changed |
2765
+ | `force_data=True` | Only data | Data needs fresh pull |
2766
+
2767
+ #### What Happens
2768
+
2769
+ When you trigger a force refresh:
2770
+
2771
+ 1. ✓ `/tmp` cache directories are deleted
2772
+ 2. ✓ Fresh data fetched from Quickbase API
2773
+ 3. ✓ Data written to S3
2774
+ 4. ✓ `/tmp` re-synced from updated S3
2775
+
2776
+ Your Lambda now has fresh data from Quickbase.
2777
+
2778
+ #### Example
2779
+
2780
+ ```python
2781
+ def lambda_handler(event, context):
2782
+ client = get_client()
2783
+ cache_mgr = get_cache_manager()
2784
+
2785
+ # Metadata changed in Quickbase? Force refresh it:
2786
+ FORCE_COMPLETE_CACHE_REFRESH_METADATA = True # ← Toggle this
2787
+
2788
+ if (FORCE_COMPLETE_CACHE_REFRESH_ALL or FORCE_COMPLETE_CACHE_REFRESH_METADATA
2789
+ or FORCE_COMPLETE_CACHE_REFRESH_DATA):
2790
+ from quickbase_extract import complete_cache_refresh
2791
+ complete_cache_refresh(
2792
+ cache_manager=cache_mgr,
2793
+ client=client,
2794
+ report_configs=get_all_reports(),
2795
+ force_metadata=FORCE_COMPLETE_CACHE_REFRESH_METADATA,
2796
+ )
2797
+
2798
+ # Rest of handler...
2799
+ ```
2800
+
2801
+ **CloudWatch logs will show:**
2802
+ ```
2803
+ WARNING: Starting complete cache refresh for: metadata (clearing /tmp, refreshing from Quickbase, updating S3...)
2804
+ DEBUG: Reset cache sync flag
2805
+ INFO: Fetching fresh data from Quickbase...
2806
+ INFO: Re-syncing /tmp from S3...
2807
+ WARNING: Complete cache refresh finished for metadata: /tmp and S3 now have fresh data from Quickbase
2808
+ ```
2809
+
2810
+ #### Important Notes
2811
+
2812
+ - **Don't leave toggles set to `True`** — revert to `False` after testing
2813
+ - **Only for development** — not a production workflow
2814
+ - Logs will show exactly what was refreshed
2815
+ - Safe to use — doesn't affect running processes, only next Lambda invocation
2816
+
2681
2817
  ## Advanced Usage
2682
2818
 
2683
2819
  ### Custom Report Configurations
@@ -855,7 +855,6 @@ s3://mit-bio-quickbase/my_project/dev/cache/report_metadata/...
855
855
  | `CACHE_BUCKET` | S3 bucket for Lambda cache | - |
856
856
  | `METADATA_STALE_HOURS` | Threshold (hours) for metadata cache staleness | `168` (7 days) |
857
857
  | `DATA_STALE_HOURS` | Threshold (hours) for data cache staleness | `24` (1 day) |
858
- | `FORCE_CACHE_REFRESH` | If set to "true", forces cache refresh on next sync | - |
859
858
 
860
859
  ### Custom Cache Location
861
860
 
@@ -929,10 +928,30 @@ def lambda_handler(event, context):
929
928
  - Cold start: Syncs cache from S3
930
929
  - Cache freshness: Auto-refreshes if stale
931
930
  - Data fetching: Queries Quickbase and caches results
931
+ - Development: Supports forcing complete cache refresh for testing
932
932
  """
933
933
  client = get_client()
934
934
  cache_mgr = get_cache_manager()
935
935
 
936
+ # OPTIONAL: Set to True to force cache refresh (for development/debugging only)
937
+ # Only one should be True at a time. force_all overrides the others.
938
+ FORCE_COMPLETE_CACHE_REFRESH_ALL = False
939
+ FORCE_COMPLETE_CACHE_REFRESH_METADATA = False
940
+ FORCE_COMPLETE_CACHE_REFRESH_DATA = False
941
+
942
+ # Force complete cache refresh if needed (dev/debugging only)
943
+ if (FORCE_COMPLETE_CACHE_REFRESH_ALL or FORCE_COMPLETE_CACHE_REFRESH_METADATA
944
+ or FORCE_COMPLETE_CACHE_REFRESH_DATA):
945
+ from quickbase_extract import complete_cache_refresh
946
+ complete_cache_refresh(
947
+ cache_manager=cache_mgr,
948
+ client=client,
949
+ report_configs=get_all_reports(),
950
+ force_all=FORCE_COMPLETE_CACHE_REFRESH_ALL,
951
+ force_metadata=FORCE_COMPLETE_CACHE_REFRESH_METADATA,
952
+ force_data=FORCE_COMPLETE_CACHE_REFRESH_DATA,
953
+ )
954
+
936
955
  # Step 1: Sync cache from S3 on cold start (only on first invocation)
937
956
  sync_from_s3_once(cache_mgr)
938
957
 
@@ -1238,7 +1257,6 @@ Checks metadata and data caches independently. Refreshes only the caches that ar
1238
1257
  **Environment Variables:**
1239
1258
  - `METADATA_STALE_HOURS`: Override default metadata staleness threshold (in hours)
1240
1259
  - `DATA_STALE_HOURS`: Override default data staleness threshold (in hours)
1241
- - `FORCE_ALL_CACHE_REFRESH`: Set to "true" to force refresh regardless of cache state
1242
1260
 
1243
1261
  **Returns:** None
1244
1262
 
@@ -1538,6 +1556,48 @@ def lambda_handler(event, context):
1538
1556
  metadata = load_report_metadata_batch(cache_mgr, config)
1539
1557
  ```
1540
1558
 
1559
+ #### `complete_cache_refresh(cache_manager, client, report_configs, force_all=False, force_metadata=False, force_data=False)`
1560
+
1561
+ Completely refresh cache for development/debugging: clear /tmp, fetch fresh from Quickbase, update S3, re-sync to /tmp.
1562
+
1563
+ This is a development utility for forcing a complete cache refresh when report metadata or configurations change. Clears local /tmp cache, fetches fresh data from Quickbase, writes to S3, and re-syncs to /tmp.
1564
+
1565
+ **Parameters:**
1566
+ - `cache_manager` (CacheManager): Cache manager instance
1567
+ - `client`: Quickbase API client for fetching fresh data
1568
+ - `report_configs` (list[ReportConfig]): List of all ReportConfig instances to refresh
1569
+ - `force_all` (bool): If True, refresh both metadata and data. Defaults to False.
1570
+ - `force_metadata` (bool): If True (and `force_all` is False), refresh only metadata. Defaults to False.
1571
+ - `force_data` (bool): If True (and `force_all` is False), refresh only data. Defaults to False.
1572
+
1573
+ **Returns:** None
1574
+
1575
+ **Raises:**
1576
+ - `Exception`: If cache clearing or refresh operations fail
1577
+
1578
+ **Example:**
1579
+ ```python
1580
+ from quickbase_extract import complete_cache_refresh
1581
+
1582
+ # Refresh only metadata (after changing report configurations)
1583
+ complete_cache_refresh(
1584
+ cache_manager=cache_mgr,
1585
+ client=qb_client,
1586
+ report_configs=get_all_reports(),
1587
+ force_metadata=True
1588
+ )
1589
+
1590
+ # Refresh all (metadata + data)
1591
+ complete_cache_refresh(
1592
+ cache_manager=cache_mgr,
1593
+ client=qb_client,
1594
+ report_configs=get_all_reports(),
1595
+ force_all=True
1596
+ )
1597
+ ```
1598
+
1599
+ **Note:** This function is designed for development/debugging. To use in Lambda, add toggles to your handler (see "Development/Debug Mode" section below).
1600
+
1541
1601
  ### Query Execution with Retry Logic
1542
1602
 
1543
1603
  #### `handle_query(client, table_id, *, select=None, where=None, sort_by=None, group_by=None, options=None, description="", max_retries=3)`
@@ -2081,43 +2141,6 @@ fields = info["fields"]
2081
2141
 
2082
2142
  ---
2083
2143
 
2084
- ### Issue: "FORCE_CACHE_REFRESH not working"
2085
-
2086
- **Symptom:** Set `FORCE_CACHE_REFRESH=true` but cache still not refreshing.
2087
-
2088
- **Cause:** Environment variable case-sensitive or not set correctly.
2089
-
2090
- **Solution:**
2091
-
2092
- 1. **Verify env var is set (case-sensitive):**
2093
- ```bash
2094
- # Must be exactly this (uppercase)
2095
- export FORCE_ALL_CACHE_REFRESH=true
2096
-
2097
- # Not these (wrong):
2098
- export force_cache_refresh=true
2099
- export Force_Cache_Refresh=true
2100
- ```
2101
-
2102
- 2. **Verify it's being read:**
2103
- ```python
2104
- import os
2105
- print(os.environ.get("FORCE_ALL_CACHE_REFRESH")) # Should print "true"
2106
- ```
2107
-
2108
- 3. **Use programmatic force instead:**
2109
- ```python
2110
- ensure_cache_freshness(
2111
- client=client,
2112
- cache_manager=cache_mgr,
2113
- report_configs_all=get_all_reports(),
2114
- report_configs_to_cache=get_reports_to_cache(),
2115
- force_all=True # Programmatic force (always works)
2116
- )
2117
- ```
2118
-
2119
- ---
2120
-
2121
2144
  ### Issue: Data fetch returns empty or different results
2122
2145
 
2123
2146
  **Symptom:** `get_data()` returns empty list or fewer records than expected.
@@ -2255,6 +2278,47 @@ ask_values = {"ask1": "value"} # No underscores
2255
2278
 
2256
2279
  ---
2257
2280
 
2281
+ ### Issue: Lambda has old cached data after I changed report metadata
2282
+
2283
+ **Symptom:** You updated a Quickbase report's fields, filters, or configuration, but your Lambda returns stale data.
2284
+
2285
+ **Cause:** Cache was loaded before your changes, and it hasn't become "stale" enough to auto-refresh yet.
2286
+
2287
+ **Solutions:**
2288
+
2289
+ 1. **Quick fix: Use force refresh toggle (development only)**
2290
+
2291
+ In your Lambda handler, temporarily set:
2292
+ ```python
2293
+ FORCE_COMPLETE_CACHE_REFRESH_METADATA = True
2294
+ ```
2295
+
2296
+ Upload new build, invoke Lambda, check logs for refresh. Then revert flag to `False`.
2297
+
2298
+ 2. **For immediate production fix:**
2299
+
2300
+ Manually delete files from S3:
2301
+ ```bash
2302
+ aws s3 rm s3://my-quickbase-cache-bucket/prod/cache/report_metadata/ --recursive
2303
+ ```
2304
+
2305
+ Next Lambda cold start will re-fetch fresh metadata.
2306
+
2307
+ 3. **To prevent in future:**
2308
+
2309
+ Reduce `metadata_stale_hours` threshold:
2310
+ ```python
2311
+ ensure_cache_freshness(
2312
+ client=client,
2313
+ cache_manager=cache_mgr,
2314
+ report_configs_all=get_all_reports(),
2315
+ report_configs_to_cache=get_reports_to_cache(),
2316
+ metadata_stale_hours=24 # Check daily instead of 30 days
2317
+ )
2318
+ ```
2319
+
2320
+ ---
2321
+
2258
2322
  ### Issue: Performance degradation over time
2259
2323
 
2260
2324
  **Symptom:** First request fast, subsequent requests slow.
@@ -2426,23 +2490,6 @@ ensure_cache_freshness(
2426
2490
  )
2427
2491
  ```
2428
2492
 
2429
- #### Environment Variable Force
2430
-
2431
- Set `FORCE_ALL_CACHE_REFRESH=true` before invoking:
2432
-
2433
- ```bash
2434
- # In Lambda environment variables or shell
2435
- export FORCE_ALL_CACHE_REFRESH=true
2436
-
2437
- # Then call normally (will force refresh automatically)
2438
- ensure_cache_freshness(
2439
- client=client,
2440
- cache_manager=cache_mgr,
2441
- report_configs_all=get_all_reports(),
2442
- report_configs_to_cache=get_reports_to_cache()
2443
- )
2444
- ```
2445
-
2446
2493
  ### Cache-All-Data Mode
2447
2494
 
2448
2495
  For production, cache data for all reports instead of a subset:
@@ -2651,6 +2698,95 @@ def scheduled_cache_refresh(event, context):
2651
2698
  return {"statusCode": 200, "message": "Cache refreshed"}
2652
2699
  ```
2653
2700
 
2701
+ ### Development/Debug Mode: Forcing Complete Cache Refresh
2702
+
2703
+ When you modify report metadata or configurations in Quickbase, your Lambda may still use stale cached data. Use the force refresh toggles to clear everything and fetch fresh data.
2704
+
2705
+ #### When to Use
2706
+
2707
+ - You changed a report's filters or fields in Quickbase
2708
+ - You added/removed fields from a report
2709
+ - You renamed a report or table
2710
+ - You need to verify fresh data is being fetched
2711
+
2712
+ #### How to Use
2713
+
2714
+ 1. Open your Lambda handler code
2715
+ 2. Set one of the toggle flags to `True`:
2716
+
2717
+ ```python
2718
+ # In lambda_handler, find these lines:
2719
+ FORCE_COMPLETE_CACHE_REFRESH_ALL = False
2720
+ FORCE_COMPLETE_CACHE_REFRESH_METADATA = False
2721
+ FORCE_COMPLETE_CACHE_REFRESH_DATA = False
2722
+
2723
+ # Change to (example: refresh only metadata):
2724
+ FORCE_COMPLETE_CACHE_REFRESH_METADATA = True
2725
+ ```
2726
+
2727
+ 3. Upload new Lambda build
2728
+ 4. Invoke your Lambda (via API or CloudWatch event)
2729
+ 5. Check CloudWatch logs for cache refresh messages
2730
+ 6. **Revert the flag back to `False`** for normal operation
2731
+
2732
+ #### Flag Options
2733
+
2734
+ | Flag | What Gets Refreshed | Use When |
2735
+ |------|---------------------|----------|
2736
+ | `force_all=True` | Both metadata + data | Complete cache overhaul needed |
2737
+ | `force_metadata=True` | Only metadata | Report configuration changed |
2738
+ | `force_data=True` | Only data | Data needs fresh pull |
2739
+
2740
+ #### What Happens
2741
+
2742
+ When you trigger a force refresh:
2743
+
2744
+ 1. ✓ `/tmp` cache directories are deleted
2745
+ 2. ✓ Fresh data fetched from Quickbase API
2746
+ 3. ✓ Data written to S3
2747
+ 4. ✓ `/tmp` re-synced from updated S3
2748
+
2749
+ Your Lambda now has fresh data from Quickbase.
2750
+
2751
+ #### Example
2752
+
2753
+ ```python
2754
+ def lambda_handler(event, context):
2755
+ client = get_client()
2756
+ cache_mgr = get_cache_manager()
2757
+
2758
+ # Metadata changed in Quickbase? Force refresh it:
2759
+ FORCE_COMPLETE_CACHE_REFRESH_METADATA = True # ← Toggle this
2760
+
2761
+ if (FORCE_COMPLETE_CACHE_REFRESH_ALL or FORCE_COMPLETE_CACHE_REFRESH_METADATA
2762
+ or FORCE_COMPLETE_CACHE_REFRESH_DATA):
2763
+ from quickbase_extract import complete_cache_refresh
2764
+ complete_cache_refresh(
2765
+ cache_manager=cache_mgr,
2766
+ client=client,
2767
+ report_configs=get_all_reports(),
2768
+ force_metadata=FORCE_COMPLETE_CACHE_REFRESH_METADATA,
2769
+ )
2770
+
2771
+ # Rest of handler...
2772
+ ```
2773
+
2774
+ **CloudWatch logs will show:**
2775
+ ```
2776
+ WARNING: Starting complete cache refresh for: metadata (clearing /tmp, refreshing from Quickbase, updating S3...)
2777
+ DEBUG: Reset cache sync flag
2778
+ INFO: Fetching fresh data from Quickbase...
2779
+ INFO: Re-syncing /tmp from S3...
2780
+ WARNING: Complete cache refresh finished for metadata: /tmp and S3 now have fresh data from Quickbase
2781
+ ```
2782
+
2783
+ #### Important Notes
2784
+
2785
+ - **Don't leave toggles set to `True`** — revert to `False` after testing
2786
+ - **Only for development** — not a production workflow
2787
+ - Logs will show exactly what was refreshed
2788
+ - Safe to use — doesn't affect running processes, only next Lambda invocation
2789
+
2654
2790
  ## Advanced Usage
2655
2791
 
2656
2792
  ### Custom Report Configurations
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "quickbase-extract"
7
- version = "0.2.0"
7
+ version = "0.3.0"
8
8
  description = "Extract and cache Quickbase report data with built-in error handling and S3 support"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"