Neon-tech commited on
Commit
b555f65
Β·
verified Β·
1 Parent(s): f9df65c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +149 -81
app.py CHANGED
@@ -19,8 +19,7 @@ OUT_DIR = "/data/tokenized"
19
  TOK_PATH = "/data/tokenizer.json"
20
  WORKER_ID = socket.gethostname()
21
  POLL_INTERVAL = 15
22
-
23
- os.makedirs(OUT_DIR, exist_ok=True)
24
 
25
  # ── Keep-alive ────────────────────────────────────────────────────────────────
26
  def serve():
@@ -45,19 +44,6 @@ def save_state(state):
45
  json.dump(state, f, indent=2)
46
  os.replace(tmp, STATE_FILE)
47
 
48
- # ── Claim a pending shard ─────────────────────────────────────────────────────
49
- def claim_shard(state):
50
- for name, info in state["shards"].items():
51
- if info["status"] == "pending":
52
- raw_path = Path(RAW_DIR) / name
53
- if raw_path.exists():
54
- info["status"] = "claimed"
55
- info["worker"] = WORKER_ID
56
- info["claimed_at"] = time.time()
57
- save_state(state)
58
- return name, raw_path
59
- return None, None
60
-
61
  # ── Tokenizer subprocess ──────────────────────────────────────────────────────
62
  _worker_tokenizer = None
63
 
@@ -108,11 +94,11 @@ def process_shard(name, raw_path, pool):
108
  tmp_path.unlink(missing_ok=True)
109
  return False, f"write_failed: {e}"
110
 
111
- tmp_path.rename(out_path) # ← atomic, only visible when complete
112
  print(f" βœ“ [{WORKER_ID}] {out_name} | {total_tokens:,} tokens")
113
  return True, None
114
 
115
- # ── Force full memory flush ───────────────────────────────────────────────────
116
  def flush_memory():
117
  gc.collect()
118
  try:
@@ -120,80 +106,162 @@ def flush_memory():
120
  except Exception:
121
  pass
122
 
123
- # ── Worker loop ───────────────────────────────────────────────────────────────
124
- def worker_loop():
125
- print(f"βœ“ [{WORKER_ID}] Loading tokenizer...")
126
- tok = Tokenizer.from_file(TOK_PATH)
127
- print(f"βœ“ [{WORKER_ID}] Tokenizer ready | vocab: {tok.get_vocab_size():,}")
128
- del tok
129
- flush_memory()
130
 
131
- pool = mp.Pool(processes=2, initializer=init_worker, initargs=(TOK_PATH,))
132
- print(f"βœ“ [{WORKER_ID}] Worker pool ready")
133
 
134
- try:
135
- while True:
136
- if not os.path.exists(STATE_FILE):
137
- print(f" [{WORKER_ID}] Waiting for state.json...")
138
- time.sleep(POLL_INTERVAL)
139
- continue
140
 
141
- try:
142
- state = load_state()
143
- except Exception as e:
144
- print(f" [{WORKER_ID}] State read error: {e}")
145
- time.sleep(POLL_INTERVAL)
146
- continue
147
 
148
- total = len(state["shards"]) + len(state.get("queue", []))
149
- done = sum(1 for v in state["shards"].values() if v["status"] == "done")
150
- if total > 0 and done == total:
151
- print(f" [{WORKER_ID}] All done. Sleeping.")
152
- time.sleep(300)
153
  continue
154
 
155
- name, raw_path = claim_shard(state)
156
-
157
- if not name:
158
- print(f" [{WORKER_ID}] Nothing ready β€” polling in {POLL_INTERVAL}s")
159
- time.sleep(POLL_INTERVAL)
 
160
  continue
161
 
162
- print(f" [{WORKER_ID}] Claimed: {name}")
163
- success, error = process_shard(name, raw_path, pool)
164
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  try:
166
- state = load_state()
167
- except Exception:
168
- pass
169
-
170
- if success:
171
- state["shards"][name]["status"] = "done"
172
- state["shards"][name]["error"] = None
173
- save_state(state)
174
- try:
175
- raw_path.unlink()
176
- print(f" [{WORKER_ID}] Deleted: {raw_path.name}")
177
- except Exception as e:
178
- print(f" [{WORKER_ID}] Delete failed: {e}")
179
- else:
180
- state["shards"][name]["status"] = "pending"
181
- state["shards"][name]["worker"] = None
182
- state["shards"][name]["claimed_at"] = None
183
- state["shards"][name]["error"] = error
184
- save_state(state)
185
- print(f" [{WORKER_ID}] Failed ({error}) β€” left on disk for retry: {name}")
186
-
187
- flush_memory()
188
- time.sleep(5)
189
-
190
- finally:
191
- pool.terminate()
192
- pool.join()
193
 
194
  # ── Entry point ───────────────────────────────────────────────────────────────
195
  if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
196
  threading.Thread(target=serve, daemon=True).start()
197
- threading.Thread(target=worker_loop, daemon=True).start()
198
- while True:
199
- time.sleep(60)
 
 
 
 
 
19
  TOK_PATH = "/data/tokenizer.json"
20
  WORKER_ID = socket.gethostname()
21
  POLL_INTERVAL = 15
22
+ SCAN_INTERVAL = 600
 
23
 
24
  # ── Keep-alive ────────────────────────────────────────────────────────────────
25
  def serve():
 
44
  json.dump(state, f, indent=2)
45
  os.replace(tmp, STATE_FILE)
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  # ── Tokenizer subprocess ──────────────────────────────────────────────────────
48
  _worker_tokenizer = None
49
 
 
94
  tmp_path.unlink(missing_ok=True)
95
  return False, f"write_failed: {e}"
96
 
97
+ tmp_path.rename(out_path)
98
  print(f" βœ“ [{WORKER_ID}] {out_name} | {total_tokens:,} tokens")
99
  return True, None
100
 
101
+ # ── Flush memory ──────────────────────────────────────────────────────────────
102
  def flush_memory():
103
  gc.collect()
104
  try:
 
106
  except Exception:
107
  pass
108
 
109
+ # ── Mop-up thread β€” just fixes state, worker_loop does the tokenizing ─────────
110
+ def mopup_thread():
111
+ while True:
112
+ time.sleep(SCAN_INTERVAL)
 
 
 
113
 
114
+ if not os.path.exists(STATE_FILE):
115
+ continue
116
 
117
+ try:
118
+ state = load_state()
119
+ except Exception:
120
+ continue
 
 
121
 
122
+ changed = False
123
+ for name, info in state["shards"].items():
124
+ status = info["status"]
125
+ raw_path = Path(RAW_DIR) / name
126
+ out_path = Path(OUT_DIR) / name.replace(".parquet", ".bin")
 
127
 
128
+ if status == "done":
 
 
 
 
129
  continue
130
 
131
+ # already tokenized but not marked done
132
+ if out_path.exists():
133
+ print(f" [MOPUP] Already tokenized, marking done: {name}")
134
+ info["status"] = "done"
135
+ info["error"] = None
136
+ changed = True
137
  continue
138
 
139
+ # reset failed/claimed back to pending if raw is on disk
140
+ if status in ("failed", "claimed") and raw_path.exists():
141
+ print(f" [MOPUP] Resetting {status} β†’ pending: {name}")
142
+ info["status"] = "pending"
143
+ info["worker"] = None
144
+ info["claimed_at"] = None
145
+ info["error"] = None
146
+ info["retries"] = 0
147
+ changed = True
148
+
149
+ # re-queue if failed and raw is gone
150
+ if status == "failed" and not raw_path.exists():
151
+ hf_path = info.get("hf_path")
152
+ queue = state.get("queue", [])
153
+ if hf_path and hf_path not in queue:
154
+ print(f" [MOPUP] Re-queuing for download: {name}")
155
+ queue.append(hf_path)
156
+ state["queue"] = queue
157
+ info["status"] = "pending"
158
+ info["worker"] = None
159
+ info["claimed_at"] = None
160
+ info["error"] = None
161
+ info["retries"] = 0
162
+ changed = True
163
+
164
+ # clean orphaned tmps
165
+ for tmp_file in list(Path(OUT_DIR).glob("*.tmp")) + list(Path(RAW_DIR).glob("*.tmp")):
166
+ print(f" [MOPUP] Removing orphaned tmp: {tmp_file.name}")
167
+ tmp_file.unlink(missing_ok=True)
168
+
169
+ if changed:
170
+ save_state(state)
171
+ print(f" [MOPUP] State updated β€” worker_loop will pick up resets")
172
+ else:
173
+ print(f" [MOPUP] Nothing to fix")
174
+
175
+ # ── Worker loop β€” continuous, same as regular tokenizer ──────────────────────
176
+ def worker_loop(pool):
177
+ while True:
178
+ if not os.path.exists(STATE_FILE):
179
+ print(f" [{WORKER_ID}] Waiting for state.json...")
180
+ time.sleep(POLL_INTERVAL)
181
+ continue
182
+
183
+ try:
184
+ state = load_state()
185
+ except Exception as e:
186
+ print(f" [{WORKER_ID}] State read error: {e}")
187
+ time.sleep(POLL_INTERVAL)
188
+ continue
189
+
190
+ total = len(state["shards"]) + len(state.get("queue", []))
191
+ done = sum(1 for v in state["shards"].values() if v["status"] == "done")
192
+ if total > 0 and done == total:
193
+ print(f" [{WORKER_ID}] All done. Sleeping.")
194
+ time.sleep(300)
195
+ continue
196
+
197
+ # claim a pending shard
198
+ claimed_name = None
199
+ claimed_path = None
200
+ for name, info in state["shards"].items():
201
+ if info["status"] == "pending":
202
+ raw_path = Path(RAW_DIR) / name
203
+ if raw_path.exists():
204
+ info["status"] = "claimed"
205
+ info["worker"] = WORKER_ID
206
+ info["claimed_at"] = time.time()
207
+ save_state(state)
208
+ claimed_name = name
209
+ claimed_path = raw_path
210
+ break
211
+
212
+ if not claimed_name:
213
+ print(f" [{WORKER_ID}] Nothing ready β€” polling in {POLL_INTERVAL}s")
214
+ time.sleep(POLL_INTERVAL)
215
+ continue
216
+
217
+ print(f" [{WORKER_ID}] Claimed: {claimed_name}")
218
+ success, error = process_shard(claimed_name, claimed_path, pool)
219
+
220
+ try:
221
+ state = load_state()
222
+ except Exception:
223
+ pass
224
+
225
+ if success:
226
+ state["shards"][claimed_name]["status"] = "done"
227
+ state["shards"][claimed_name]["error"] = None
228
+ save_state(state)
229
  try:
230
+ claimed_path.unlink()
231
+ print(f" [{WORKER_ID}] Deleted: {claimed_path.name}")
232
+ except Exception as e:
233
+ print(f" [{WORKER_ID}] Delete failed: {e}")
234
+ else:
235
+ retries = state["shards"][claimed_name].get("retries", 0) + 1
236
+ state["shards"][claimed_name]["retries"] = retries
237
+ state["shards"][claimed_name]["error"] = error
238
+ state["shards"][claimed_name]["worker"] = None
239
+ state["shards"][claimed_name]["claimed_at"] = None
240
+ state["shards"][claimed_name]["status"] = "failed" if retries >= 3 else "pending"
241
+ save_state(state)
242
+ print(f" [{WORKER_ID}] Failed ({error}) retry {retries}/3: {claimed_name}")
243
+
244
+ flush_memory()
245
+ time.sleep(5)
 
 
 
 
 
 
 
 
 
 
 
246
 
247
  # ── Entry point ───────────────────────────────────────────────────────────────
248
  if __name__ == "__main__":
249
+ os.makedirs(OUT_DIR, exist_ok=True)
250
+
251
+ print(f"βœ“ [{WORKER_ID}] Loading tokenizer...")
252
+ tok = Tokenizer.from_file(TOK_PATH)
253
+ print(f"βœ“ [{WORKER_ID}] Tokenizer ready | vocab: {tok.get_vocab_size():,}")
254
+ del tok
255
+ flush_memory()
256
+
257
+ pool = mp.Pool(processes=2, initializer=init_worker, initargs=(TOK_PATH,))
258
+ print(f"βœ“ [{WORKER_ID}] Worker pool ready")
259
+
260
  threading.Thread(target=serve, daemon=True).start()
261
+ threading.Thread(target=mopup_thread, daemon=True).start()
262
+
263
+ try:
264
+ worker_loop(pool)
265
+ finally:
266
+ pool.terminate()
267
+ pool.join()