Optimize draft sheet batch writes to single API call

- Changed write_picks_batch() from 105 individual API calls to 1 batch call
- Builds 2D array covering full pick range and writes in single update_values()
- Eliminates Google Sheets 429 rate limiting during resync operations
- Reduces resync time from ~2 minutes to seconds

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2025-12-12 15:01:48 -06:00
parent 637f1a015f
commit a814aadd61
2 changed files with 36 additions and 24 deletions

View File

@ -1 +1 @@
2.24.0 2.24.1

View File

@ -129,6 +129,8 @@ class DraftSheetService(SheetsService):
Write multiple draft picks to the sheet in a single batch operation. Write multiple draft picks to the sheet in a single batch operation.
Used for resync operations to repopulate the entire sheet from database. Used for resync operations to repopulate the entire sheet from database.
Uses true batch updates to write all picks in a single API call,
avoiding rate limiting issues.
Args: Args:
season: Draft season number season: Draft season number
@ -169,41 +171,51 @@ class DraftSheetService(SheetsService):
self._config.draft_sheet_worksheet self._config.draft_sheet_worksheet
) )
# Sort picks by overall to write in order # Sort picks by overall to find range bounds
sorted_picks = sorted(picks, key=lambda p: p[0]) sorted_picks = sorted(picks, key=lambda p: p[0])
# Build batch data - each pick goes to its calculated row # Find min and max overall to determine row range
# We'll write one row at a time to handle non-contiguous picks min_overall = sorted_picks[0][0]
success_count = 0 max_overall = sorted_picks[-1][0]
failure_count = 0
# Build a 2D array for the entire range (sparse - empty rows for missing picks)
# Row index 0 = min_overall, row index N = max_overall
num_rows = max_overall - min_overall + 1
batch_data: List[List[str]] = [['', '', '', ''] for _ in range(num_rows)]
# Populate the batch data array
for overall, orig_owner, owner, player_name, swar in sorted_picks: for overall, orig_owner, owner, player_name, swar in sorted_picks:
try: row_index = overall - min_overall
pick_data = [[orig_owner, owner, player_name, swar]] batch_data[row_index] = [orig_owner, owner, player_name, str(swar)]
row = overall + 1
start_column = self._config.draft_sheet_start_column
cell_range = f'{start_column}{row}'
await loop.run_in_executor( # Calculate the cell range for the batch write
None, start_row = min_overall + 1 # +1 for header row
lambda cr=cell_range, pd=pick_data: worksheet.update_values( start_column = self._config.draft_sheet_start_column
crange=cr, values=pd end_column = chr(ord(start_column) + 3) # 4 columns: D -> G
) end_row = max_overall + 1
)
success_count += 1 cell_range = f'{start_column}{start_row}:{end_column}{end_row}'
except Exception as e:
self.logger.error(f"Failed to write pick {overall}: {e}")
failure_count += 1
self.logger.info( self.logger.info(
f"Batch write complete: {success_count} succeeded, {failure_count} failed", f"Writing {len(picks)} picks in single batch to range {cell_range}",
season=season
)
# Write all picks in a single API call
await loop.run_in_executor(
None,
lambda: worksheet.update_values(crange=cell_range, values=batch_data)
)
self.logger.info(
f"Batch write complete: {len(picks)} picks written successfully",
season=season, season=season,
total_picks=len(picks) total_picks=len(picks)
) )
return (success_count, failure_count) return (len(picks), 0)
except Exception as e: except Exception as e:
self.logger.error(f"Failed to initialize batch write: {e}", season=season) self.logger.error(f"Failed batch write: {e}", season=season)
return (0, len(picks)) return (0, len(picks))
async def clear_picks_range( async def clear_picks_range(