AI Agent commited on
Commit
130f6cd
·
1 Parent(s): 87e6e59

Fix multiuser concurrency bottleneck and update HF deployment docs

Browse files
Files changed (3) hide show
  1. NITDAA_HF_DEPLOYMENT_GUIDE.md +5 -2
  2. app.py +5 -18
  3. pipeline/graph_store.py +31 -21
NITDAA_HF_DEPLOYMENT_GUIDE.md CHANGED
@@ -10,10 +10,13 @@ This document outlines the specific steps and constraints required to deploy the
10
  - Hardware allocation: Free Tier (2 vCPU, 16GB Disk, 12GB RAM).
11
 
12
  ## 2. HuggingFace Secrets Configuration
13
- To enable dynamic Dataset Synchronization, you must configure the following variable in your Space's Settings under **Variables and Secrets**:
14
- - `HF_TOKEN`: A HuggingFace access token with "Read" and "Write" permissions to the `Sam-max1/he-data` and `Sam-max1/mat_data` datasets.
15
  - `ADMIN_MODE`: Set to `0` to completely lock down the UI for public usage.
16
 
 
 
 
17
  ## 3. Deployment Steps
18
  1. Clone the `nitdaa` folder locally.
19
  2. Initialize a git repository pointing to your HF Space remote.
 
10
  - Hardware allocation: Free Tier (2 vCPU, 16GB Disk, 12GB RAM).
11
 
12
  ## 2. HuggingFace Secrets Configuration
13
+ To enable dynamic Dataset Synchronization, you must configure the following variable:
14
+ - `HF_TOKEN`: A HuggingFace access token with "Read" and "Write" permissions to the `Sam-max1/he-data` and `Sam-max1/mat_data` datasets. This token is available via the system keyring or environment variables.
15
  - `ADMIN_MODE`: Set to `0` to completely lock down the UI for public usage.
16
 
17
+ ## 2.1 Minimalist Deployment Rule
18
+ Nitdaa must always contain **only the bare minimum files required** to run the application in HF and the bare minimum files to be pushed to HF. Exclude all extraneous assets, development scripts, or unnecessary documentation from the Hugging Face repository.
19
+
20
  ## 3. Deployment Steps
21
  1. Clone the `nitdaa` folder locally.
22
  2. Initialize a git repository pointing to your HF Space remote.
app.py CHANGED
@@ -131,23 +131,9 @@ _jobs: dict[str, dict] = {}
131
  _active_graph_tasks = 0
132
  _session_uploads: dict[str, int] = {}
133
 
134
- # Global query queue to prevent LLM overload
135
- import queue as _global_queue
136
- _llm_query_queue = _global_queue.Queue()
137
-
138
- def _query_worker():
139
- while True:
140
- task = _llm_query_queue.get()
141
- if task is None: break
142
- func, args = task
143
- try:
144
- func(*args)
145
- except Exception as e:
146
- log.exception("Query worker error")
147
- finally:
148
- _llm_query_queue.task_done()
149
-
150
- threading.Thread(target=_query_worker, daemon=True).start()
151
 
152
  # Auto-ingest background progress tracker
153
  _auto_ingest_status: dict = {
@@ -879,7 +865,8 @@ def query_start():
879
  _query_jobs[job_id]["done"] = True
880
  log_query_summary(token, remote_addr, q, top_k or 10, 0, False, str(e))
881
 
882
- _llm_query_queue.put((_run, ()))
 
883
  return jsonify({"job_id": job_id})
884
 
885
  @app.route("/api/query/stream/<job_id>")
 
131
  _active_graph_tasks = 0
132
  _session_uploads: dict[str, int] = {}
133
 
134
+ # Global thread pool to allow concurrent RAG execution
135
+ from concurrent.futures import ThreadPoolExecutor
136
+ _query_executor = ThreadPoolExecutor(max_workers=8)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
  # Auto-ingest background progress tracker
139
  _auto_ingest_status: dict = {
 
865
  _query_jobs[job_id]["done"] = True
866
  log_query_summary(token, remote_addr, q, top_k or 10, 0, False, str(e))
867
 
868
+ _query_executor.submit(_run)
869
+
870
  return jsonify({"job_id": job_id})
871
 
872
  @app.route("/api/query/stream/<job_id>")
pipeline/graph_store.py CHANGED
@@ -9,38 +9,48 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
9
  import config
10
 
11
  _db = None
12
- _conn = None
13
  _db_lock = threading.Lock()
14
  _schema_initialized = False
15
-
16
 
17
  def _get_conn():
18
- global _db, _conn, _schema_initialized
19
  if not getattr(config, 'GRAPH_AVAILABLE', True):
20
  return None
 
 
 
21
 
22
  with _db_lock:
23
- if _conn is not None:
24
- return _conn
25
- try:
26
- import kuzu
27
- # Ensure parent path exists
28
- os.makedirs(os.path.dirname(config.KUZU_DB_PATH), exist_ok=True)
29
- _db = kuzu.Database(config.KUZU_DB_PATH)
30
- _conn = kuzu.Connection(_db)
31
- print(f"[GraphStore] Connected to Kuzu at {config.KUZU_DB_PATH}")
32
- if not _schema_initialized:
33
- _init_schema()
34
- _schema_initialized = True
35
- except Exception as e:
36
- print(f"[GraphStore] Kuzu initialization failed. ({e})")
37
- _conn = None
38
- return _conn
39
 
 
 
 
 
 
 
 
40
 
41
- def _init_schema():
 
42
  """Create node and relationship tables for Kuzu."""
43
- conn = _conn
 
44
  if not conn:
45
  return
46
 
 
9
  import config
10
 
11
  _db = None
 
12
  _db_lock = threading.Lock()
13
  _schema_initialized = False
14
+ _local = threading.local()
15
 
16
  def _get_conn():
17
+ global _db, _schema_initialized
18
  if not getattr(config, 'GRAPH_AVAILABLE', True):
19
  return None
20
+
21
+ if hasattr(_local, 'conn') and _local.conn is not None:
22
+ return _local.conn
23
 
24
  with _db_lock:
25
+ if _db is None:
26
+ try:
27
+ import kuzu
28
+ os.makedirs(os.path.dirname(config.KUZU_DB_PATH), exist_ok=True)
29
+ _db = kuzu.Database(config.KUZU_DB_PATH)
30
+ print(f"[GraphStore] Connected to Kuzu at {config.KUZU_DB_PATH}")
31
+
32
+ # Use a temporary connection to initialize schema
33
+ tmp_conn = kuzu.Connection(_db)
34
+ if not _schema_initialized:
35
+ _init_schema(tmp_conn)
36
+ _schema_initialized = True
37
+ except Exception as e:
38
+ print(f"[GraphStore] Kuzu initialization failed. ({e})")
39
+ return None
 
40
 
41
+ try:
42
+ import kuzu
43
+ _local.conn = kuzu.Connection(_db)
44
+ return _local.conn
45
+ except Exception as e:
46
+ print(f"[GraphStore] Failed to create thread-local Kuzu connection. ({e})")
47
+ return None
48
 
49
+
50
+ def _init_schema(conn=None):
51
  """Create node and relationship tables for Kuzu."""
52
+ if not conn:
53
+ conn = _get_conn()
54
  if not conn:
55
  return
56