SeaWolf-AI commited on
Commit
77a208f
ยท
verified ยท
1 Parent(s): b6dc399

mirror ledger to private dataset

Browse files
Files changed (2) hide show
  1. app/ledger.py +81 -1
  2. app/main.py +22 -4
app/ledger.py CHANGED
@@ -34,6 +34,84 @@ ROOT = _root()
34
  LOCK = threading.Lock()
35
 
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  def boot_check():
38
  """์›์žฅ์ด ์žฌ์‹œ์ž‘์„ ๊ฒฌ๋””๋Š”์ง€ **์‹ค์ธก**ํ•œ๋‹ค.
39
 
@@ -107,7 +185,8 @@ def register(user_id, strategy, ptype, assets):
107
  "visits": prev.get("visits", []),
108
  }
109
  _write(_p("users.json"), u)
110
- return u[user_id]
 
111
 
112
 
113
  def user_by_key(key):
@@ -143,6 +222,7 @@ def submit(user_id, asset, position, source="api"):
143
  with LOCK:
144
  with open(_p("positions", "%s.jsonl" % asset), "a", encoding="utf-8") as f:
145
  f.write(json.dumps(rec, ensure_ascii=False) + "\n")
 
146
  touch(user_id)
147
  return rec
148
 
 
34
  LOCK = threading.Lock()
35
 
36
 
37
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ์›์žฅ ๋ฏธ๋Ÿฌ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
38
+ # ๐Ÿ”ด HF Space์˜ /data ๋Š” ์˜์† ์Šคํ† ๋ฆฌ์ง€๋ฅผ ๋ถ™์ด์ง€ ์•Š์œผ๋ฉด ์žฌ์‹œ์ž‘์— ์‚ฌ๋ผ์ง„๋‹ค.
39
+ # ์‹ค์ธกํ–ˆ๋‹ค โ€” ์žฌ์‹œ์ž‘ ํ›„ ๋ถ€ํŒ… ํ‘œ์‹์ด ์—†์–ด์กŒ๋‹ค(boots 1 -> 1).
40
+ # ์œ ๋ฃŒ ์Šคํ† ๋ฆฌ์ง€ ๋Œ€์‹  ๋น„๊ณต๊ฐœ ๋ฐ์ดํ„ฐ์…‹ ์ €์žฅ์†Œ์— ์›์žฅ์„ ๋ฏธ๋Ÿฌ๋งํ•œ๋‹ค.
41
+ # ์‹œ์ฆŒ 4๊ฐœ์›”์น˜ ๊ธฐ๋ก์ด ์ปจํ…Œ์ด๋„ˆ ์žฌ์‹œ์ž‘ ํ•œ ๋ฒˆ์— ๋‚ ์•„๊ฐ€๋ฉด ๋Œ€ํšŒ๊ฐ€ ๋๋‚œ๋‹ค.
42
+ MIRROR = os.environ.get("FINCHAL_MIRROR") # ์˜ˆ: FINAL-Bench/finchal-ledger
43
+ _MIR = {"ok": False, "dirty": False, "at": 0.0, "n": 0, "err": None}
44
+
45
+
46
+ def _api():
47
+ from huggingface_hub import HfApi
48
+ tok = os.environ.get("HF_TOKEN") or os.environ.get("FINCHAL_HF_TOKEN")
49
+ return HfApi(token=tok) if tok else None
50
+
51
+
52
+ def restore():
53
+ """๋ถ€ํŒ… ์‹œ ๋ฏธ๋Ÿฌ์—์„œ ์›์žฅ์„ ๋˜์‚ด๋ฆฐ๋‹ค. ์ด๊ฒŒ ์‹คํŒจํ•˜๋ฉด ๋ฏธ๋Ÿฌ์— ์“ฐ์ง€ ์•Š๋Š”๋‹ค.
54
+
55
+ ๐Ÿ”‘ ๋ณต์› ์‹คํŒจ ํ›„ flush ํ•˜๋ฉด ๋นˆ ์›์žฅ์ด ๋ฏธ๋Ÿฌ๋ฅผ ๋ฎ์–ด ์‹œ์ฆŒ์ด ํ†ต์งธ๋กœ ์‚ฌ๋ผ์ง„๋‹ค.
56
+ ํ•œ ๋ฒˆ์˜ ์ž˜๋ชป๋œ ๋ฎ์–ด์“ฐ๊ธฐ๊ฐ€ 4๊ฐœ์›”์„ ์ง€์šด๋‹ค.
57
+ """
58
+ if not MIRROR:
59
+ return "๋ฏธ๋Ÿฌ ์—†์Œ"
60
+ try:
61
+ from huggingface_hub import snapshot_download
62
+ a = _api()
63
+ if a is None:
64
+ _MIR["err"] = "HF_TOKEN ์—†์Œ"
65
+ return _MIR["err"]
66
+ try:
67
+ a.repo_info(MIRROR, repo_type="dataset")
68
+ except Exception:
69
+ a.create_repo(MIRROR, repo_type="dataset", private=True, exist_ok=True)
70
+ d = snapshot_download(MIRROR, repo_type="dataset",
71
+ token=a.token, local_dir=None)
72
+ import shutil, glob
73
+ os.makedirs(os.path.join(ROOT, "positions"), exist_ok=True)
74
+ n = 0
75
+ for src in glob.glob(os.path.join(d, "**", "*"), recursive=True):
76
+ if not os.path.isfile(src) or os.sep + ".cache" in src:
77
+ continue
78
+ rel = os.path.relpath(src, d)
79
+ if rel.startswith(".git"):
80
+ continue
81
+ dst = os.path.join(ROOT, rel)
82
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
83
+ shutil.copyfile(src, dst)
84
+ n += 1
85
+ _MIR["ok"] = True
86
+ _MIR["n"] = n
87
+ return "๋ณต์› %d๊ฐœ" % n
88
+ except Exception as e:
89
+ _MIR["err"] = "%s: %s" % (type(e).__name__, e)
90
+ return _MIR["err"]
91
+
92
+
93
+ def flush(force=False):
94
+ """์›์žฅ์„ ๋ฏธ๋Ÿฌ์— ์˜ฌ๋ฆฐ๋‹ค. ์ œ์ถœ๋งˆ๋‹ค ์˜ฌ๋ฆฌ๋ฉด ์ปค๋ฐ‹์ด ํญ์ฃผํ•˜๋ฏ€๋กœ ๋ชจ์•„์„œ ๋ณด๋‚ธ๋‹ค."""
95
+ if not (MIRROR and _MIR["ok"]) or (not _MIR["dirty"] and not force):
96
+ return False
97
+ try:
98
+ a = _api()
99
+ a.upload_folder(repo_id=MIRROR, repo_type="dataset", folder_path=ROOT,
100
+ commit_message="ledger sync",
101
+ ignore_patterns=["*.tmp", ".cache/*"])
102
+ _MIR["dirty"] = False
103
+ _MIR["at"] = time.time()
104
+ return True
105
+ except Exception as e:
106
+ _MIR["err"] = "%s: %s" % (type(e).__name__, e)
107
+ return False
108
+
109
+
110
+ def mirror_status():
111
+ return {"repo": MIRROR, "restored": _MIR["ok"], "files": _MIR["n"],
112
+ "pending": _MIR["dirty"], "error": _MIR["err"]}
113
+
114
+
115
  def boot_check():
116
  """์›์žฅ์ด ์žฌ์‹œ์ž‘์„ ๊ฒฌ๋””๋Š”์ง€ **์‹ค์ธก**ํ•œ๋‹ค.
117
 
 
185
  "visits": prev.get("visits", []),
186
  }
187
  _write(_p("users.json"), u)
188
+ _MIR["dirty"] = True
189
+ return u[user_id]
190
 
191
 
192
  def user_by_key(key):
 
222
  with LOCK:
223
  with open(_p("positions", "%s.jsonl" % asset), "a", encoding="utf-8") as f:
224
  f.write(json.dumps(rec, ensure_ascii=False) + "\n")
225
+ _MIR["dirty"] = True
226
  touch(user_id)
227
  return rec
228
 
app/main.py CHANGED
@@ -336,14 +336,31 @@ def _anchor_loop():
336
  time.sleep(6 * 3600)
337
 
338
 
 
 
 
339
  _DUR = L.boot_check()
340
 
341
 
 
 
 
 
 
 
 
 
 
 
 
342
  @app.on_event("startup")
343
  def _startup():
 
 
 
 
344
  if os.environ.get("FINCHAL_NO_ANCHOR"):
345
  return
346
- import threading
347
  threading.Thread(target=_anchor_loop, daemon=True).start()
348
 
349
 
@@ -351,6 +368,7 @@ def _startup():
351
  def health():
352
  return {"ok": True, "season": S1["name"], "assets": list(ASSETS),
353
  "ledger": L.ROOT, "durability": _DUR[0], "boots": _DUR[1],
354
- "warn": None if _DUR[0] == "ํ™•์ธ๋จ" else
355
- "์›์žฅ์ด ์žฌ์‹œ์ž‘์„ ๊ฒฌ๋””๋Š”์ง€ ์•„์ง ํ™•์ธ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. "
356
- "์˜์† ์Šคํ† ๋ฆฌ์ง€ ์—†์ด ์‹œ์ฆŒ์„ ์—ด๋ฉด ๊ธฐ๋ก์ด ์‚ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."}
 
 
336
  time.sleep(6 * 3600)
337
 
338
 
339
+ # ๐Ÿ”‘ ์›์žฅ ๋ณต์›์€ **๋‹ค๋ฅธ ๋ฌด์—‡๋ณด๋‹ค ๋จผ์ €**๋‹ค. ๋ณต์› ์ „์— ๋ถ€ํŒ… ํ‘œ์‹์„ ์“ฐ๋ฉด
340
+ # ๊ทธ ํ‘œ์‹์ด ๋ฏธ๋Ÿฌ๋ฅผ ๋ฎ์–ด ์›์žฅ์„ ์ง€์šด๋‹ค.
341
+ _RESTORE = L.restore()
342
  _DUR = L.boot_check()
343
 
344
 
345
+ def _flush_loop():
346
+ """์›์žฅ์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ฏธ๋Ÿฌ์— ์˜ฌ๋ฆฐ๋‹ค. ์ œ์ถœ๋งˆ๋‹ค ์ปค๋ฐ‹ํ•˜๋ฉด ์ €์žฅ์†Œ๊ฐ€ ํ„ฐ์ง„๋‹ค."""
347
+ import traceback
348
+ while True:
349
+ time.sleep(90)
350
+ try:
351
+ L.flush()
352
+ except Exception:
353
+ traceback.print_exc()
354
+
355
+
356
  @app.on_event("startup")
357
  def _startup():
358
+ import threading
359
+ print("[finchal] ์›์žฅ %s ยท ๋ฏธ๋Ÿฌ %s" % (L.ROOT, _RESTORE))
360
+ if L.MIRROR:
361
+ threading.Thread(target=_flush_loop, daemon=True).start()
362
  if os.environ.get("FINCHAL_NO_ANCHOR"):
363
  return
 
364
  threading.Thread(target=_anchor_loop, daemon=True).start()
365
 
366
 
 
368
  def health():
369
  return {"ok": True, "season": S1["name"], "assets": list(ASSETS),
370
  "ledger": L.ROOT, "durability": _DUR[0], "boots": _DUR[1],
371
+ "mirror": L.mirror_status(), "restore": _RESTORE,
372
+ "warn": None if (_DUR[0] == "ํ™•์ธ๋จ" or L.mirror_status()["restored"])
373
+ else "์›์žฅ์ด ์žฌ์‹œ์ž‘์„ ๊ฒฌ๋”˜๋‹ค๋Š” ์ฆ๊ฑฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. "
374
+ "๋ฏธ๋Ÿฌ(FINCHAL_MIRROR)๋‚˜ ์˜์† ์Šคํ† ๋ฆฌ์ง€๋ฅผ ๋ถ™์ด์‹ญ์‹œ์˜ค."}