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.
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:
season: Draft season number
@ -169,41 +171,51 @@ class DraftSheetService(SheetsService):
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])
# Build batch data - each pick goes to its calculated row
# We'll write one row at a time to handle non-contiguous picks
success_count = 0
failure_count = 0
# Find min and max overall to determine row range
min_overall = sorted_picks[0][0]
max_overall = sorted_picks[-1][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:
try:
pick_data = [[orig_owner, owner, player_name, swar]]
row = overall + 1
start_column = self._config.draft_sheet_start_column
cell_range = f'{start_column}{row}'
row_index = overall - min_overall
batch_data[row_index] = [orig_owner, owner, player_name, str(swar)]
await loop.run_in_executor(
None,
lambda cr=cell_range, pd=pick_data: worksheet.update_values(
crange=cr, values=pd
)
)
success_count += 1
except Exception as e:
self.logger.error(f"Failed to write pick {overall}: {e}")
failure_count += 1
# Calculate the cell range for the batch write
start_row = min_overall + 1 # +1 for header row
start_column = self._config.draft_sheet_start_column
end_column = chr(ord(start_column) + 3) # 4 columns: D -> G
end_row = max_overall + 1
cell_range = f'{start_column}{start_row}:{end_column}{end_row}'
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,
total_picks=len(picks)
)
return (success_count, failure_count)
return (len(picks), 0)
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))
async def clear_picks_range(