Query Performance Profiler

Overview

Query Performance Profiler הוא כלי ניטור לשאילתות MongoDB איטיות, המספק:

  1. זיהוי שאילתות איטיות – מעקב בזמן אמת אחרי שאילתות שחורגות מסף זמן מוגדר

  2. ניתוח Explain Plans – הצגה ויזואלית של תוכנית הביצוע של MongoDB (כולל Aggregation Pipelines)

  3. המלצות אופטימיזציה – הצעות אוטומטיות לשיפור ביצועים

  4. היסטוריית שאילתות – שמירה וניתוח של דפוסי שאילתות לאורך זמן

קהל יעד

הפרופיילר מיועד ל-Admin בלבד. גישה אליו דורשת:

  • הרשאת Admin ב-WebApp, או

  • טוקן ייעודי (X-Profiler-Token)

מה הכלי לא עושה

  • לא מחליף את MongoDB Profiler המובנה ברמת ה-DB

  • לא מספק אופטימיזציה אוטומטית (רק המלצות)

  • לא מיועד ל-Production Debugging בזמן אמת של שאילתות בודדות

ממשק משתמש (WebApp)

הנתיב

GET /admin/profiler

איך להגיע

  1. דרך Settings → כלי אדמין → Query Profiler

  2. או ישירות לכתובת /admin/profiler

מה רואים בדשבורד

אזור

תיאור

Summary

סיכום כללי: מספר שאילתות איטיות, זמן ממוצע, collections מושפעים

Slow Queries Table

טבלה עם שאילתות איטיות, כולל סינון לפי collection

ניתוח Query/Pipeline

טופס להזנת שאילתה לניתוח מיידי

Explain Visualization

ויזואליזציה של שלבי הביצוע (COLLSCAN, IXSCAN, FETCH וכו«)

Recommendations

המלצות אופטימיזציה לפי חומרה (קריטי/אזהרה/מידע)

הערה

ברירת המחדל של ה-verbosity היא queryPlanner (בטוח) – לא מריץ את השאילתה בפועל.

API Reference

Authentication

אם PROFILER_AUTH_TOKEN מוגדר, יש לשלוח את הטוקן בכותרת:

X-Profiler-Token: <your-token>

אם הטוקן לא מוגדר, הגישה מתבססת על הרשאת Admin ב-WebApp Session.

Endpoints

Method

Endpoint

תיאור

GET

/api/profiler/summary

סיכום מצב הפרופיילר

GET

/api/profiler/slow-queries

רשימת שאילתות איטיות (עם סינון)

POST

/api/profiler/explain

הרצת Explain Plan על שאילתה/pipeline

POST

/api/profiler/recommendations

ניתוח והמלצות לשאילתה

POST

/api/profiler/analyze

Alias ל-recommendations

GET

/api/profiler/collection/<name>/stats

סטטיסטיקות collection (גודל, אינדקסים)

GET /api/profiler/summary

מחזיר סיכום כללי:

curl -H "X-Profiler-Token: $TOKEN" \
     https://your-app.com/api/profiler/summary

Response:

{
  "status": "success",
  "data": {
    "total_slow_queries": 42,
    "collections_affected": ["code_snippets", "users"],
    "avg_execution_time_ms": 350.5,
    "max_execution_time_ms": 2500.0,
    "unique_patterns": 15,
    "threshold_ms": 100
  }
}

GET /api/profiler/slow-queries

Query Parameters:

Parameter

תיאור

ברירת מחדל

limit

מספר שאילתות להחזיר

50

collection

סינון לפי collection

(הכל)

min_time

זמן ביצוע מינימלי (ms)

(הכל)

hours

שאילתות מהשעות האחרונות

(הכל)

דוגמה:

curl -H "X-Profiler-Token: $TOKEN" \
     "https://your-app.com/api/profiler/slow-queries?limit=20&collection=code_snippets&hours=24"

POST /api/profiler/explain

מריץ Explain Plan על שאילתה או Aggregation Pipeline.

Body (Query):

{
  "collection": "code_snippets",
  "query": {"user_id": "123", "is_deleted": false},
  "verbosity": "queryPlanner"
}

Body (Pipeline):

{
  "collection": "code_snippets",
  "pipeline": [
    {"$match": {"user_id": "123"}},
    {"$group": {"_id": "$language", "count": {"$sum": 1}}}
  ],
  "verbosity": "queryPlanner"
}

דוגמה:

curl -X POST \
     -H "X-Profiler-Token: $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"collection":"code_snippets","query":{"user_id":"<value>"}}' \
     https://your-app.com/api/profiler/explain

POST /api/profiler/recommendations

מחזיר Explain Plan + המלצות אופטימיזציה:

curl -X POST \
     -H "X-Profiler-Token: $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"collection":"code_snippets","query":{"user_id":"<value>"}}' \
     https://your-app.com/api/profiler/recommendations

GET /api/profiler/collection/<name>/stats

מחזיר סטטיסטיקות collection:

curl -H "X-Profiler-Token: $TOKEN" \
     https://your-app.com/api/profiler/collection/code_snippets/stats

Response:

{
  "status": "success",
  "data": {
    "size_bytes": 1048576,
    "count": 5000,
    "avg_obj_size": 210,
    "index_count": 3,
    "indexes": ["_id_", "user_id_1", "user_id_1_is_deleted_1"],
    "total_index_size": 524288
  }
}

Security

Authentication

שכבה

משתנה/מנגנון

תיאור

Token

PROFILER_AUTH_TOKEN

טוקן נשלח ב-Header X-Profiler-Token

IP Allowlist

PROFILER_ALLOWED_IPS

רשימת IPs מורשים (CSV)

Rate Limit

PROFILER_RATE_LIMIT

מגבלת בקשות לדקה (ברירת מחדל: 60)

Admin Session

WebApp

אם אין Token, נדרשת הרשאת Admin

הגדרת Token

# .env
PROFILER_AUTH_TOKEN=my-secure-profiler-token
PROFILER_ALLOWED_IPS=127.0.0.1,10.0.0.1

אזהרת Observer Effect

אזהרה

Observer Effect – הרצת explain("executionStats") או explain("allPlansExecution") מריצה את השאילתה בפועל!

הסיכונים:

  • אם השאילתה איטית כי היא מעמיסה על ה-CPU, הרצת ה-Explain תכפיל את העומס

  • אם השאילתה נועלת מסמכים (write operations), זה עלול להחמיר את המצב

  • ב-Production עמוס, הרצה אוטומטית של explain יכולה ליצור ”אפקט שלג“

המלצות:

  1. השתמש ב-``queryPlanner`` כברירת מחדל – לא מריץ את השאילתה, רק מציג את התוכנית

  2. הרץ ``executionStats`` רק לפי דרישה – כפי שממומש בכפתור ”נתח“ בדשבורד

  3. אל תריץ explain אוטומטית לכל שאילתה איטית – זה יכפיל את הבעיה

  4. שקול הרצת explain בשעות שפל או על replica secondary

רמות Verbosity

רמה

תיאור

מתי להשתמש

queryPlanner

תוכנית בלבד, ללא הרצה

לבדיקת אינדקסים (בטוח)

executionStats

כולל סטטיסטיקות ביצוע

ניתוח ביצועים מלא

allPlansExecution

כל התוכניות שנבחנו

Debug מתקדם בלבד

Privacy / PII

חשוב

נרמול שאילתות מונע דליפת מידע אישי (PII)

הפונקציה _normalize_query_shape מחליפה את כל הערכים בפלייסהולדרים:

  • ערכים פשוטים → <value>

  • מערכים → <N items>

  • null → <null>

דוגמה:

# Query מקורי (לא מוצג)
{"email": "john@example.com", "status": {"$in": ["active", "pending"]}}

# Query מנורמל (מה שמוצג בדשבורד)
{"email": "<value>", "status": {"$in": ["<2 items>"]}}

אזהרה

אל תתעד או תציג דוגמאות עם נתוני אמת/PII בדוחות או בלוגים.

Persistence

Collection

שם: slow_queries_log

TTL Index

מחיקה אוטומטית אחרי 7 ימים:

db.slow_queries_log.createIndex(
  {"timestamp": 1},
  {expireAfterSeconds: 604800, name: "ttl_cleanup"}
)

אינדקסים נוספים

// חיפוש מהיר לפי collection + זמן
db.slow_queries_log.createIndex(
  {"collection": 1, "timestamp": -1},
  {name: "collection_timestamp"}
)

// חיפוש לפי דפוס שאילתה
db.slow_queries_log.createIndex(
  {"query_id": 1},
  {name: "query_pattern"}
)

Metrics (Prometheus)

מטריקות זמינות כאשר PROFILER_METRICS_ENABLED=true:

Metric

Type

תיאור

mongodb_slow_queries_total

Counter

מספר שאילתות איטיות לפי collection ו-operation

mongodb_query_duration_seconds

Histogram

התפלגות זמני שאילתות

mongodb_collscan_detected_total

Counter

מספר COLLSCAN שזוהו

query_profiler_buffer_size

Gauge

מספר שאילתות בבאפר הזיכרון

Environment Variables

ראו את הטבלה המלאה ב-משתני סביבה - רפרנס.

משתנה

תיאור

ברירת מחדל

PROFILER_ENABLED

הפעלת Query Performance Profiler

true

PROFILER_SLOW_THRESHOLD_MS

סף זמן (ms) להגדרת ”שאילתה איטית“

100

PROFILER_MAX_BUFFER_SIZE

מספר שאילתות בזיכרון

1000

PROFILER_AUTH_TOKEN

טוקן גישה (Header X-Profiler-Token)

(ריק)

PROFILER_ALLOWED_IPS

Allowlist של IPs (CSV)

(ריק)

PROFILER_RATE_LIMIT

מגבלת בקשות לדקה

60

PROFILER_METRICS_ENABLED

הפעלת מטריקות Prometheus

true

המלצות אופטימיזציה נפוצות

בעיה

סימפטום

המלצה

🔴 COLLSCAN

stage: "COLLSCAN"

צור אינדקס על שדות הסינון

🟠 Sort בזיכרון

stage: "SORT"

הוסף שדה מיון לאינדקס

🟡 יחס יעילות נמוך

docsExamined >> nReturned

שפר selectivity של האינדקס

🔴 $lookup ללא אינדקס

nestedLoopJoin

צור אינדקס על ה-foreign field

🟠 $sort משתמש בדיסק

usedDisk: true

הוסף $match לפני ה-$sort

קישורים נוספים