idolezal commited on
Commit
97092f5
1 Parent(s): 9d17838

Improved safety of submission race condition

Browse files
Files changed (2) hide show
  1. app.py +6 -3
  2. server.py +44 -28
app.py CHANGED
@@ -61,7 +61,7 @@ def process_submission(*inputs):
61
 
62
  gr.Info('Submission valid, running tournament...')
63
 
64
- leaderboard_server.prepare_model_for_submission(inputs["submission_file"], metadata)
65
  except ValueError as err:
66
  gr.Warning(str(err))
67
  return (
@@ -82,6 +82,7 @@ def process_submission(*inputs):
82
  gr.update(visible=False),
83
  gr.update(visible=False),
84
  )
 
85
  return (
86
  gr.update(visible=False),
87
  gr.update(visible=True),
@@ -89,7 +90,7 @@ def process_submission(*inputs):
89
  gr.update(interactive=True, visible=True),
90
  gr.update(visible=True),
91
  gr.update(
92
- value=leaderboard_server.get_leaderboard(leaderboard_server.pre_submit.tournament_results),
93
  visible=True,
94
  datatype="markdown",
95
  elem_classes="leaderboard-table",
@@ -116,7 +117,9 @@ def submit_results():
116
 
117
 
118
  def erase_pre_submit():
119
- leaderboard_server.pre_submit = None
 
 
120
  return (
121
  gr.update(value='Pre-submit model', visible=True, interactive=True),
122
  gr.update(visible=False),
 
61
 
62
  gr.Info('Submission valid, running tournament...')
63
 
64
+ pre_submit = leaderboard_server.prepare_model_for_submission(inputs["submission_file"], metadata)
65
  except ValueError as err:
66
  gr.Warning(str(err))
67
  return (
 
82
  gr.update(visible=False),
83
  gr.update(visible=False),
84
  )
85
+
86
  return (
87
  gr.update(visible=False),
88
  gr.update(visible=True),
 
90
  gr.update(interactive=True, visible=True),
91
  gr.update(visible=True),
92
  gr.update(
93
+ value=leaderboard_server.get_leaderboard(pre_submit),
94
  visible=True,
95
  datatype="markdown",
96
  elem_classes="leaderboard-table",
 
117
 
118
 
119
  def erase_pre_submit():
120
+ with leaderboard_server.pre_submit_lock:
121
+ if leaderboard_server.pre_submit:
122
+ leaderboard_server.pre_submit = None # NOTE: Is it safe? How to confirm that `submission_id` is equal?
123
  return (
124
  gr.update(value='Pre-submit model', visible=True, interactive=True),
125
  gr.update(visible=False),
server.py CHANGED
@@ -8,6 +8,7 @@ import time
8
  import requests
9
  from collections import namedtuple
10
  from xml.sax.saxutils import escape as xmlEscape, quoteattr as xmlQuoteAttr
 
11
 
12
  import gradio as gr
13
  import pandas as pd
@@ -90,6 +91,8 @@ def check_significance(model_a_path, model_b_path):
90
  result = check_significance_wait_for_result(result_url)
91
  return result
92
 
 
 
93
  class LeaderboardServer:
94
  def __init__(self):
95
  self.server_address = REPO
@@ -107,6 +110,7 @@ class LeaderboardServer:
107
  self.submission_ids = set()
108
  self.fetch_existing_models()
109
  self.tournament_results = self.load_tournament_results()
 
110
  self.pre_submit = None
111
 
112
  def update_leaderboard(self):
@@ -139,8 +143,8 @@ class LeaderboardServer:
139
 
140
  self.submission_id_to_file[submission_id] = submission_file
141
 
142
- def get_leaderboard(self, tournament_results=None, category=None):
143
- tournament_results = tournament_results if tournament_results else self.tournament_results
144
  category = category if category else self.tasks_category_overall
145
 
146
  if len(tournament_results) == 0:
@@ -150,8 +154,8 @@ class LeaderboardServer:
150
  for submission_id in tournament_results.keys():
151
  path = self.submission_id_to_file.get(submission_id)
152
  if path is None:
153
- if self.pre_submit and submission_id == self.pre_submit.submission_id:
154
- data = json.load(open(self.pre_submit.file))
155
  else:
156
  raise gr.Error(f"Internal error: Submission [{submission_id}] not found")
157
  elif path:
@@ -211,7 +215,7 @@ class LeaderboardServer:
211
  local_results["model_type"] = data["metadata"]["model_type"]
212
  local_results["parameters"] = data["metadata"]["parameters"]
213
 
214
- if self.pre_submit and submission_id == self.pre_submit.submission_id:
215
  processed_results.insert(0, local_results)
216
  else:
217
  processed_results.append(local_results)
@@ -318,7 +322,7 @@ class LeaderboardServer:
318
 
319
  PreSubmit = namedtuple('PreSubmit', 'tournament_results, submission_id, file')
320
 
321
- def prepare_model_for_submission(self, file, metadata) -> None:
322
  with open(file, "r") as f:
323
  data = json.load(f)
324
 
@@ -335,32 +339,44 @@ class LeaderboardServer:
335
  with open(file, "w") as f:
336
  json.dump(data, f, separators=(',', ':')) # compact JSON
337
 
338
- tournament_results = self.start_tournament(submission_id, file)
339
- self.pre_submit = self.PreSubmit(tournament_results, submission_id, file)
 
 
 
 
 
 
 
 
340
 
341
  def save_pre_submit(self):
342
- if self.pre_submit:
343
- tournament_results, submission_id, file = self.pre_submit
344
- api.upload_file(
345
- path_or_fileobj=file,
346
- path_in_repo=f"data/{submission_id}.json",
347
- repo_id=self.server_address,
348
- repo_type=self.repo_type,
349
- token=HF_TOKEN,
350
- )
 
 
351
 
352
- # Temporary save tournament results
353
- tournament_results_path = os.path.join(self.local_leaderboard, "tournament.json")
354
- with open(tournament_results_path, "w") as f:
355
- json.dump(tournament_results, f, sort_keys=True, indent=2) # readable JSON
356
 
357
- api.upload_file(
358
- path_or_fileobj=tournament_results_path,
359
- path_in_repo="tournament.json",
360
- repo_id=self.server_address,
361
- repo_type=self.repo_type,
362
- token=HF_TOKEN,
363
- )
 
 
364
 
365
  def get_model_detail(self, submission_id):
366
  path = self.submission_id_to_file.get(submission_id)
 
8
  import requests
9
  from collections import namedtuple
10
  from xml.sax.saxutils import escape as xmlEscape, quoteattr as xmlQuoteAttr
11
+ from threading import Lock
12
 
13
  import gradio as gr
14
  import pandas as pd
 
91
  result = check_significance_wait_for_result(result_url)
92
  return result
93
 
94
+ pre_submit_lock = Lock()
95
+
96
  class LeaderboardServer:
97
  def __init__(self):
98
  self.server_address = REPO
 
110
  self.submission_ids = set()
111
  self.fetch_existing_models()
112
  self.tournament_results = self.load_tournament_results()
113
+ self.pre_submit_lock = pre_submit_lock
114
  self.pre_submit = None
115
 
116
  def update_leaderboard(self):
 
143
 
144
  self.submission_id_to_file[submission_id] = submission_file
145
 
146
+ def get_leaderboard(self, pre_submit=None, category=None):
147
+ tournament_results = pre_submit.tournament_results if pre_submit else self.tournament_results
148
  category = category if category else self.tasks_category_overall
149
 
150
  if len(tournament_results) == 0:
 
154
  for submission_id in tournament_results.keys():
155
  path = self.submission_id_to_file.get(submission_id)
156
  if path is None:
157
+ if pre_submit and submission_id == pre_submit.submission_id:
158
+ data = json.load(open(pre_submit.file))
159
  else:
160
  raise gr.Error(f"Internal error: Submission [{submission_id}] not found")
161
  elif path:
 
215
  local_results["model_type"] = data["metadata"]["model_type"]
216
  local_results["parameters"] = data["metadata"]["parameters"]
217
 
218
+ if pre_submit and submission_id == pre_submit.submission_id:
219
  processed_results.insert(0, local_results)
220
  else:
221
  processed_results.append(local_results)
 
322
 
323
  PreSubmit = namedtuple('PreSubmit', 'tournament_results, submission_id, file')
324
 
325
+ def prepare_model_for_submission(self, file, metadata) -> PreSubmit:
326
  with open(file, "r") as f:
327
  data = json.load(f)
328
 
 
339
  with open(file, "w") as f:
340
  json.dump(data, f, separators=(',', ':')) # compact JSON
341
 
342
+ while True:
343
+ with self.pre_submit_lock:
344
+ if self.pre_submit == None:
345
+ tournament_results = self.start_tournament(submission_id, file)
346
+ self.pre_submit = self.PreSubmit(tournament_results, submission_id, file)
347
+ break
348
+ gr.Info("Waiting in queue...")
349
+ time.sleep(10)
350
+
351
+ return self.pre_submit
352
 
353
  def save_pre_submit(self):
354
+ with self.pre_submit_lock:
355
+ if self.pre_submit:
356
+ tournament_results, submission_id, file = self.pre_submit
357
+
358
+ api.upload_file(
359
+ path_or_fileobj=file,
360
+ path_in_repo=f"data/{submission_id}.json",
361
+ repo_id=self.server_address,
362
+ repo_type=self.repo_type,
363
+ token=HF_TOKEN,
364
+ )
365
 
366
+ # Temporary save tournament results
367
+ tournament_results_path = os.path.join(self.local_leaderboard, "tournament.json")
368
+ with open(tournament_results_path, "w") as f:
369
+ json.dump(tournament_results, f, sort_keys=True, indent=2) # readable JSON
370
 
371
+ api.upload_file(
372
+ path_or_fileobj=tournament_results_path,
373
+ path_in_repo="tournament.json",
374
+ repo_id=self.server_address,
375
+ repo_type=self.repo_type,
376
+ token=HF_TOKEN,
377
+ )
378
+
379
+ self.pre_submit = None
380
 
381
  def get_model_detail(self, submission_id):
382
  path = self.submission_id_to_file.get(submission_id)