Skip to content

Commit e678216

Browse files
committed
fix(reshard): split active courses into separate 3MB shards
Active courses now use a 3MB per-shard target (instead of 5MB) so frequently-changing data produces smaller update payloads. Stable courses remain at 5MB. Factorised the packing logic into a shared _pack() helper used by both groups.
1 parent 7b8b0ff commit e678216

1 file changed

Lines changed: 34 additions & 36 deletions

File tree

scripts/reshard.py

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
from src.data import crypto_box
4343

4444
# ── Config ─────────────────────────────────────────────────────────────────
45-
SHARD_TARGET_COMPRESSED = 5 * 1024 * 1024 # 5 MB per shard (compressed)
45+
STABLE_SHARD_TARGET_COMPRESSED = 5 * 1024 * 1024 # 5 MB per stable shard
46+
ACTIVE_SHARD_TARGET_COMPRESSED = 3 * 1024 * 1024 # 3 MB per active shard
4647
ACTIVE_WINDOW_DAYS = 14 # lectures updated within this window → "active"
4748

4849

@@ -141,43 +142,40 @@ def main():
141142
print(f"Stable courses: {len(stable)} ({sum(c['size'] for c in stable)/1024:.0f}KB)")
142143

143144
# ── Step 2: Group into shards ─────────────────────────────────────────
144-
target_uncompressed = SHARD_TARGET_COMPRESSED * COMPRESSION_RATIO_GUESS
145145
groups: list[list[str]] = []
146146

147-
def _flush(group: list[str], current_size: int) -> None:
148-
if group:
149-
groups.append(list(group))
150-
151-
# Group 1: each active course → its own shard (if big) or grouped with
152-
# other active courses
153-
active_remaining = list(active)
154-
while active_remaining:
155-
group: list[str] = []
156-
group_size = 0
157-
while active_remaining:
158-
c = active_remaining[0]
159-
sz = c["size"]
160-
if group and group_size + sz > target_uncompressed:
161-
break
162-
group.append(c["course_id"])
163-
group_size += sz
164-
active_remaining.pop(0)
165-
_flush(group, group_size)
166-
167-
# Group 2: pack stable courses with first-fit
168-
remaining = list(stable)
169-
while remaining:
170-
group: list[str] = []
171-
group_size = 0
172-
for c in list(remaining):
173-
sz = c["size"]
174-
if group_size + sz <= target_uncompressed:
175-
group.append(c["course_id"])
176-
group_size += sz
177-
remaining.remove(c)
178-
# If a single course exceeds the target, it still gets its own
179-
# shard (courses are never split)
180-
_flush(group, group_size)
147+
def _pack(courses: list[dict], target: int) -> list[list[str]]:
148+
"""First-fit pack course_ids into shards at most ``target`` bytes.
149+
A course exceeding the target gets its own shard (never split)."""
150+
result: list[list[str]] = []
151+
remaining = list(courses)
152+
while remaining:
153+
group: list[str] = []
154+
group_size = 0
155+
for c in list(remaining):
156+
sz = c["size"]
157+
if group_size + sz <= target:
158+
group.append(c["course_id"])
159+
group_size += sz
160+
remaining.remove(c)
161+
# Single oversized course → own shard (loop will pick it up
162+
# on next iteration when group is empty).
163+
result.append(group)
164+
return result
165+
166+
# Active courses → 3 MB shards (they change frequently, small shards
167+
# minimize update payload).
168+
active_target = ACTIVE_SHARD_TARGET_COMPRESSED * COMPRESSION_RATIO_GUESS
169+
for g in _pack(active, active_target):
170+
if g:
171+
groups.append(g)
172+
173+
# Stable courses → 5 MB shards (they never change, larger shards mean
174+
# fewer files with stable hashes).
175+
stable_target = STABLE_SHARD_TARGET_COMPRESSED * COMPRESSION_RATIO_GUESS
176+
for g in _pack(stable, stable_target):
177+
if g:
178+
groups.append(g)
181179

182180
if not groups:
183181
groups = [[]] # at least one (empty) shard

0 commit comments

Comments
 (0)