Cion-lab commited on
Commit
e11175e
·
verified ·
1 Parent(s): 9b3e027

review round: pointer written+read-back before prune, verify retried, latest rolled to final, stale-pointer scan on resume, ddp_timeout 4h, chunked batched commits, manifest self-consistency gate, full content verification after upload

Browse files
Files changed (1) hide show
  1. train/hubckpt.py +64 -18
train/hubckpt.py CHANGED
@@ -211,8 +211,29 @@ def ensure_repo(repo, api, token=None, repo_type="dataset"):
211
  "files_before": len(have)}
212
 
213
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  def push_and_prune(repo, dirpath, path_in_repo, api, repo_type="dataset", token=None,
215
- prune=True, min_free_gb=6.0):
216
  """One full §3.13 cycle. Raises if verification fails and pruning was requested -- the local copy is
217
  kept in exactly that case, because the Hub copy is not yet proven to exist."""
218
  files = sorted(os.listdir(dirpath))
@@ -224,26 +245,28 @@ def push_and_prune(repo, dirpath, path_in_repo, api, repo_type="dataset", token=
224
  up = api.upload_folder(repo_id=repo, folder_path=dirpath, path_in_repo=path_in_repo,
225
  repo_type=repo_type, commit_message=f"checkpoint {path_in_repo}",
226
  token=token)
227
- ver = verify_checkpoint(repo, dirpath, path_in_repo, token=token, repo_type=repo_type)
 
 
 
 
 
 
 
 
 
 
 
228
  res = {"uploaded_to": getattr(up, "commit_url", str(up)[:120]), "verify": ver,
229
- "free_before_gb": round(free_before / 1e9, 2)}
 
230
  if prune:
231
  if not ver["ok"]:
232
- res["pruned"] = False
233
  res["prune_blocked_reason"] = (
234
  f"hub verification failed ({len(ver['missing'])} missing, "
235
  f"{len(ver['mismatch'])} mismatched) -- keeping the local copy in {dirpath}")
236
  raise RuntimeError(res["prune_blocked_reason"])
237
- shutil.rmtree(dirpath, ignore_errors=True)
238
- res["pruned"] = True
239
- res["local_dir_exists_after"] = os.path.exists(dirpath)
240
- res["free_after_gb"] = round(free_bytes() / 1e9, 2)
241
- res["disk_ok"] = res["free_after_gb"] >= min_free_gb
242
- if prune and not res["disk_ok"]:
243
- # Free space is a training-blocking metric (§3.13), not a footnote: say so loudly here rather
244
- # than letting the next checkpoint fail with an OSError.
245
- res["disk_warning"] = (f"only {res['free_after_gb']} GB free after pruning "
246
- f"(floor {min_free_gb} GB)")
247
  return res
248
 
249
 
@@ -311,10 +334,33 @@ def download_checkpoint(repo, path_in_repo, dest, api, repo_type="dataset", toke
311
  "dir": dirs.pop(), "requested_dest": dest, "path_in_repo": path_in_repo}
312
 
313
 
314
- def write_latest_pointer(repo, cur, api, repo_type="dataset", token=None):
 
 
 
 
 
 
 
315
  p = os.path.join("/tmp", "latest.json")
316
  with open(p, "w") as f:
317
  json.dump(cur, f, indent=1, sort_keys=True)
318
- api.upload_file(path_or_fileobj=p, path_in_repo="latest.json", repo_id=repo,
319
- repo_type=repo_type, commit_message=f"latest -> {cur.get('step')}", token=token)
320
- return p
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  "files_before": len(have)}
212
 
213
 
214
+ def prune_local(dirpath, res, min_free_gb=6.0):
215
+ """Delete a local checkpoint whose Hub copy has been verified, and record the space that freed.
216
+
217
+ Separate from `push_and_prune` because the caller has to roll `latest.json` and read it back *between*
218
+ the verification and the deletion: pruning first would destroy the only copy the pointer should be
219
+ naming, and a pointer commit that failed silently would then be unrecoverable.
220
+ """
221
+ shutil.rmtree(dirpath, ignore_errors=True)
222
+ res["pruned"] = True
223
+ res["local_dir_exists_after"] = os.path.exists(dirpath)
224
+ res["free_after_gb"] = round(free_bytes() / 1e9, 2)
225
+ res["disk_ok"] = res["free_after_gb"] >= min_free_gb
226
+ if not res["disk_ok"]:
227
+ # Free space is a training-blocking metric (§3.13), not a footnote: say so loudly here rather
228
+ # than letting the next checkpoint fail with an OSError.
229
+ res["disk_warning"] = (f"only {res['free_after_gb']} GB free after pruning "
230
+ f"(floor {min_free_gb} GB)")
231
+ print(f" DISK WARNING: {res['disk_warning']}", flush=True)
232
+ return res
233
+
234
+
235
  def push_and_prune(repo, dirpath, path_in_repo, api, repo_type="dataset", token=None,
236
+ prune=True, min_free_gb=6.0, verify_tries=3):
237
  """One full §3.13 cycle. Raises if verification fails and pruning was requested -- the local copy is
238
  kept in exactly that case, because the Hub copy is not yet proven to exist."""
239
  files = sorted(os.listdir(dirpath))
 
245
  up = api.upload_folder(repo_id=repo, folder_path=dirpath, path_in_repo=path_in_repo,
246
  repo_type=repo_type, commit_message=f"checkpoint {path_in_repo}",
247
  token=token)
248
+ # Retry the verification, not the upload. `verify_checkpoint` reads the tree immediately after the
249
+ # commit, and E-033 showed this Hub path answers transiently under load; treating a propagation race
250
+ # as a content problem would kill a billed session over a checkpoint that is actually fine.
251
+ ver = None
252
+ for attempt in range(1, verify_tries + 1):
253
+ ver = verify_checkpoint(repo, dirpath, path_in_repo, token=token, repo_type=repo_type)
254
+ if ver["ok"]:
255
+ break
256
+ if attempt < verify_tries:
257
+ print(f" verify attempt {attempt}: {len(ver['missing'])} missing, "
258
+ f"{len(ver['mismatch'])} mismatched -- re-checking in {20 * attempt}s", flush=True)
259
+ __import__("time").sleep(20 * attempt)
260
  res = {"uploaded_to": getattr(up, "commit_url", str(up)[:120]), "verify": ver,
261
+ "free_before_gb": round(free_before / 1e9, 2), "pruned": False,
262
+ "free_after_gb": round(free_bytes() / 1e9, 2)}
263
  if prune:
264
  if not ver["ok"]:
 
265
  res["prune_blocked_reason"] = (
266
  f"hub verification failed ({len(ver['missing'])} missing, "
267
  f"{len(ver['mismatch'])} mismatched) -- keeping the local copy in {dirpath}")
268
  raise RuntimeError(res["prune_blocked_reason"])
269
+ prune_local(dirpath, res, min_free_gb)
 
 
 
 
 
 
 
 
 
270
  return res
271
 
272
 
 
334
  "dir": dirs.pop(), "requested_dest": dest, "path_in_repo": path_in_repo}
335
 
336
 
337
+ def write_latest_pointer(repo, cur, api, repo_type="dataset", token=None, retries=3):
338
+ """Roll `latest.json` and prove the Hub now serves it.
339
+
340
+ The read-back is not ceremony: the caller prunes its local copy immediately afterwards, so a pointer
341
+ commit that silently failed would leave a Hub-verified checkpoint that nothing points at, and the next
342
+ session would resume from the previous one and re-read ~100 M tokens with every self-consistency guard
343
+ passing (weights, cursor, fingerprint and seed all agree with each other -- only the pointer is wrong).
344
+ """
345
  p = os.path.join("/tmp", "latest.json")
346
  with open(p, "w") as f:
347
  json.dump(cur, f, indent=1, sort_keys=True)
348
+ want_step = cur.get("step")
349
+ last = None
350
+ for attempt in range(1, retries + 1):
351
+ try:
352
+ api.upload_file(path_or_fileobj=p, path_in_repo="latest.json", repo_id=repo,
353
+ repo_type=repo_type, commit_message=f"latest -> {want_step}", token=token)
354
+ except Exception as e:
355
+ last = f"upload {type(e).__name__}: {str(e)[:120]}"
356
+ else:
357
+ back = latest_pointer(repo, repo_type=repo_type, token=token)
358
+ if back.get("step") == want_step and back.get("path_in_repo") == cur.get("path_in_repo"):
359
+ return p
360
+ last = f"hub reports {back.get('step')!r} at {back.get('path_in_repo')!r}, " \
361
+ f"we wrote {want_step!r} at {cur.get('path_in_repo')!r} ({str(back)[:120]})"
362
+ print(f" latest.json attempt {attempt} failed: {last}; retrying", flush=True)
363
+ __import__("time").sleep(15 * attempt)
364
+ raise RuntimeError(f"latest.json never landed at step {want_step} on {repo} after {retries} "
365
+ f"attempts: {last}. Refusing to prune the local copy that this pointer should "
366
+ "point at.")