1433), and credentials. SQL Auth and Windows Auth are both supported.VIEW SERVER STATE and VIEW DATABASE STATE on the monitored instance. It never writes to your SQL Server.| Tier | Instances | Duration | Sentinel AI | All Modules | Best For |
|---|---|---|---|---|---|
| Monitor | 3 | 15 days | Text only | ✗ | Small teams, single SQL Server |
| Intelligence | 10 | 15 days | Voice + all tabs | ✓ | Mid-size DBA teams, multiple instances |
| Enterprise | Unlimited | 30 days | Voice + priority | ✓ | Large estates, multi-team rollout |
dpsmf.ai/trial.html. No credit card is required.DPSMF-XXXXXX-XXXXXX-TRIAL license key. Copy it immediately — it is also emailed to you, but the email may take a few minutes to arrive. You will need the key on first login.you+monitor@company.com). Trial accounts cannot be upgraded mid-trial — you can convert to a paid subscription at any time.dpsmf.ai/dpsmf-login.html or click Log In from any DPSMF page. Enter the email and password you registered with.DPSMF-XXXXXX-XXXXXX-TRIAL key and click Activate. The key is validated server-side and tied to your account — it cannot be used again./app/. The dashboard is initially empty — no instances are connected yet. The trial countdown begins at this point.SELECT @@VERSION) to confirm network and credentials. Common failures: firewall blocking port 1433, incorrect hostname, login not mapped to a database. See Requirements for the full permission list.master, tempdb, msdb) are always included for wait-stat and config analysis.VIEW SERVER STATE (for DMVs), VIEW DATABASE STATE (per-DB), VIEW ANY DEFINITION (execution plans). Read the Requirements panel for the exact GRANT script.DPSMF does not generate recommendations immediately. The Intelligence Engine needs to observe a representative sample of workload patterns — typically one full business day — before the Knowledge Graph can distinguish normal from anomalous behaviour.
During the baseline period you can still use Sentinel in Query Mode — ask questions like "What are my top wait types right now?" or "Show me execution plans for queries over 5 seconds." These are answered directly from live DMV data without requiring a trained baseline.
After the baseline window closes, Anomaly Detection activates and the alert system comes fully online. Here is what to expect on Day 2:
alerts@dpsmf.ai. Add this address to your allowlist to avoid spam filtering.sys.dm_os_wait_stats and classifies waits into CPU, I/O, lock, network, and memory categories. Deltas are computed every 10 seconds.avg CPU ms, avg reads, or execution count to find the culprit fast.cpu_pct, batch_requests_sec, or page_life_expectancy. Set the operator and threshold, then choose a notification channel. The baseline-aware option fires only when the value deviates from learned normal — not just crosses a static number.| Signal | What It Means | Action |
|---|---|---|
cpu_pct > 85% sustained | CPU-bound workload or runaway query | Open Top Queries, sort by avg CPU ms |
| PLE dropping > 20% in 10 min | Memory pressure from large sort/hash | Check memory grants in Top Queries |
| Batch rate drops 30%+ vs baseline | Blocking or connectivity issue | Open Blocking Chain view in Sentinel |
| PAGEIOLATCH waits spike | Missing index or cold buffer pool | See Index & Query Health module |
| RESOURCE_SEMAPHORE dominates | Insufficient memory for query grants | Review max server memory setting in Configuration Intelligence |
These are the actual DMV queries DPSMF runs on your SQL Server instance. You can run them directly in SSMS to cross-check what the dashboard is showing.
Wait Category Snapshot (delta from last 10 s)
-- Top wait types right now (excludes benign idle waits)
SELECT TOP 10
wait_type,
waiting_tasks_count,
wait_time_ms,
max_wait_time_ms,
signal_wait_time_ms,
wait_time_ms - signal_wait_time_ms AS resource_wait_ms
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN (
'SLEEP_TASK','WAITFOR','BROKER_TO_FLUSH','BROKER_TASK_STOP',
'CLR_AUTO_EVENT','DISPATCHER_QUEUE_SEMAPHORE','FT_IFTS_SCHEDULER_IDLE_WAIT',
'HADR_WORK_QUEUE','ONDEMAND_TASK_QUEUE','REQUEST_FOR_DEADLOCK_SEARCH',
'RESOURCE_QUEUE','SERVER_IDLE_CHECK','SLEEP_DBSTARTUP','SLEEP_DCOMSTARTUP',
'SLEEP_MASTERDBREADY','SLEEP_MASTERMDREADY','SLEEP_MASTERUPGRADED',
'SLEEP_MSDBSTARTUP','SLEEP_TEMPDBSTARTUP','SNI_HTTP_ACCEPT',
'SP_SERVER_DIAGNOSTICS_SLEEP','SQLTRACE_BUFFER_FLUSH','SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
'WAIT_XTP_OFFLINE_CKPT_NEW_LOG','XE_DISPATCHER_WAIT','XE_TIMER_EVENT'
)
ORDER BY wait_time_ms DESC;
Page Life Expectancy per NUMA Node
SELECT
object_name,
counter_name,
instance_name,
cntr_value AS page_life_expectancy_sec
FROM sys.dm_os_performance_counters
WHERE counter_name = 'Page life expectancy'
AND object_name LIKE '%Buffer Manager%'
OR (object_name LIKE '%Buffer Node%' AND counter_name = 'Page life expectancy')
ORDER BY object_name;
Batch Requests/sec + CPU Utilisation
-- Batch request rate (compare two snapshots 10 s apart for rate)
SELECT cntr_value AS batch_requests_total
FROM sys.dm_os_performance_counters
WHERE counter_name = 'Batch Requests/sec';
-- SQL Server CPU utilisation (ring buffer, last 256 records)
SELECT TOP 1
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') AS sql_cpu_pct,
record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS system_idle_pct,
GETDATE() AS sampled_at
FROM (
SELECT CONVERT(XML, record) AS record
FROM sys.dm_os_ring_buffers
WHERE ring_buffer_type = 'RING_BUFFER_SCHEDULER_MONITOR'
AND record LIKE '%%'
) AS rb
ORDER BY rb.record.value('(./Record/@id)[1]', 'bigint') DESC;
CONTROL SERVER) and cross-database permission chains.VIEW SERVER STATE and VIEW ANY DEFINITION permissions. If your monitoring account is missing VIEW ANY DEFINITION, TDE and permission sprawl scans will return partial results. Check Settings → Instances → Permission Check to validate.CONTROL SERVER, ALTER ANY LOGIN, or db_owner on a sensitive database is highlighted in amber.| Dimension | Weight | What Drops It |
|---|---|---|
| Identity hygiene | 25% | Orphaned logins, stale accounts, disabled users with active grants |
| Privilege minimisation | 30% | Non-SA sysadmin members, CONTROL SERVER grants, nested role escalation |
| Encryption coverage | 20% | Unencrypted databases, expired TDE certificates, missing backup encryption |
| Authentication hygiene | 15% | Mixed-mode auth on domain instances, SQL logins without password policy |
| Audit trail | 10% | SQL Audit not configured, C2 audit disabled, no login failure logging |
Run these in SSMS against any monitored instance to verify what DPSMF’s Security Audit Engine will detect.
Orphaned Logins — Server Logins With No Matching DB User
-- Logins that exist at server level but have no user mapping in any database
SELECT sp.name AS login_name, sp.type_desc, sp.is_disabled, sp.create_date
FROM sys.server_principals sp
WHERE sp.type IN ('S','U','G') -- SQL, Windows, Group logins
AND sp.name NOT LIKE '##%' -- exclude internal accounts
AND sp.name NOT IN ('sa','guest')
AND NOT EXISTS (
SELECT 1 FROM sys.server_role_members srm
WHERE srm.member_principal_id = sp.principal_id
)
AND NOT EXISTS (
SELECT 1 FROM sys.database_principals dp
WHERE dp.sid = sp.sid
)
ORDER BY sp.create_date DESC;
Sysadmin Members (Who Has the Keys?)
SELECT sp.name AS principal_name, sp.type_desc, sp.is_disabled,
sp.default_database_name, sp.create_date, sp.modify_date
FROM sys.server_role_members srm
JOIN sys.server_principals sp ON sp.principal_id = srm.member_principal_id
WHERE srm.role_principal_id = SUSER_ID('sysadmin')
ORDER BY sp.is_disabled, sp.name;
TDE Status Across All Databases
SELECT
d.name AS database_name,
d.state_desc,
dek.encryption_state_desc,
CASE dek.encryption_state WHEN 3 THEN 'Encrypted' ELSE 'NOT Encrypted' END AS tde_status,
c.name AS cert_name,
c.expiry_date AS cert_expiry,
DATEDIFF(DAY, GETDATE(), c.expiry_date) AS days_until_expiry
FROM sys.databases d
LEFT JOIN sys.dm_database_encryption_keys dek ON dek.database_id = d.database_id
LEFT JOIN sys.certificates c ON c.thumbprint = dek.encryptor_thumbprint
WHERE d.database_id > 4 -- exclude system DBs
ORDER BY dek.encryption_state, d.name;
Logins Inactive for 90+ Days
-- Requires SQL Server 2008 R2+ and VIEW SERVER STATE
SELECT sp.name AS login_name, sp.type_desc, sp.is_disabled,
sl.last_successful_logon, sl.last_unsuccessful_logon,
DATEDIFF(DAY, sl.last_successful_logon, GETDATE()) AS days_since_login
FROM sys.server_principals sp
LEFT JOIN sys.dm_exec_sessions ses ON ses.login_name = sp.name
LEFT JOIN sys.dm_exec_connections con ON con.session_id = ses.session_id
CROSS APPLY (
SELECT MAX(login_time) AS last_successful_logon,
NULL AS last_unsuccessful_logon
FROM sys.dm_exec_sessions WHERE login_name = sp.name
) sl
WHERE sp.type IN ('S','U')
AND (sl.last_successful_logon IS NULL OR sl.last_successful_logon < DATEADD(DAY,-90,GETDATE()))
AND sp.name NOT IN ('sa','guest')
ORDER BY days_since_login DESC;
VIEW SERVER STATE. On SQL Server 2019+ you can also query sys.dm_os_last_successful_logons for more reliable data, as sys.dm_exec_sessions only covers currently active sessions.slow queries after index rebuild or blocking on tempdb. Results are ranked by semantic similarity to your query, not keyword match.| Scenario | What to Search | What You’ll Find |
|---|---|---|
| Query suddenly slow after deploy | plan regression after schema change | Parameter sniffing nodes, plan cache flush triggers, UPDATE STATISTICS guidance |
| Tempdb contention alerts firing | tempdb allocation contention | GAM/SGAM page contention nodes, trace flag 1118/1117 analysis, file count recommendations |
| Memory pressure on busy OLTP | memory grant timeout OLTP | Resource semaphore nodes, max server memory tuning, memory-optimised table guidance |
| AG failover investigation | availability group unexpected failover | Health check timeout nodes, lease timeout chain, network jitter patterns |
These are real Knowledge Graph search queries with the type of nodes and recommendations they surface. Try them in Intelligence → Knowledge Graph → Search.
tempdb contention high waitssys.dm_tran_active_snapshot_database_transactionsALTER SERVER CONFIGURATION SET MEMORY_OPTIMIZED TEMPDB_METADATA = ON)query slow after statistics updateOPTION (OPTIMIZE FOR UNKNOWN) or plan guideUPDATE STATISTICS ... WITH FULLSCAN then force recompileALTER DATABASE SCOPED CONFIGURATION SET LEGACY_CARDINALITY_ESTIMATION = ON to testavailability group unexpected failoverLeaseTimeout (default 20 s); usually caused by OS scheduler starvation or VM pauseSameSubnetDelay and SameSubnetThreshold cluster parameterssys.dm_db_missing_index_details continuously, but de-duplicates and scores candidates by projected impact (avg user seeks × avg total cost) rather than just listing them raw.sys.dm_db_index_physical_stats across all user databases. Results are stored historically so you can see fragmentation growth over time, not just a point-in-time snapshot.REORGANIZE candidates; larger indexes are marked REBUILD. Use the maintenance window filter to schedule rebuilds outside peak hours.| Fragmentation % | Page Count | Recommended Action |
|---|---|---|
| < 5% | Any | No action needed |
| 5–30% | < 1,000 pages | ALTER INDEX ... REORGANIZE (online, low impact) |
| 5–30% | ≥ 1,000 pages | ALTER INDEX ... REBUILD WITH (ONLINE=ON) if Enterprise |
| > 30% | Any | ALTER INDEX ... REBUILD — schedule in maintenance window |
| Any | < 100 pages | Ignore — small tables don’t benefit from fragmentation fixes |
These are the queries DPSMF uses to surface missing index candidates, fragmentation data, and top queries. Cross-check the dashboard against your own SSMS session.
Top Missing Index Candidates (by Projected Impact)
SELECT TOP 20
DB_NAME(mid.database_id) AS database_name,
OBJECT_NAME(mid.object_id, mid.database_id) AS table_name,
migs.avg_total_user_cost * migs.avg_user_impact
* (migs.user_seeks + migs.user_scans) AS impact_score,
migs.user_seeks, migs.user_scans,
mid.equality_columns,
mid.inequality_columns,
mid.included_columns,
'CREATE INDEX [IX_' + OBJECT_NAME(mid.object_id, mid.database_id)
+ '_missing] ON [' + mid.statement + '] ('
+ ISNULL(mid.equality_columns,'')
+ CASE WHEN mid.inequality_columns IS NOT NULL
THEN ',' + mid.inequality_columns ELSE '' END
+ ')'
+ CASE WHEN mid.included_columns IS NOT NULL
THEN ' INCLUDE (' + mid.included_columns + ')' ELSE '' END
+ ';' AS create_index_statement
FROM sys.dm_db_missing_index_group_stats migs
JOIN sys.dm_db_missing_index_groups mig ON mig.index_group_handle = migs.group_handle
JOIN sys.dm_db_missing_index_details mid ON mid.index_handle = mig.index_handle
WHERE migs.avg_total_user_cost * migs.avg_user_impact
* (migs.user_seeks + migs.user_scans) > 100 -- filter low-impact noise
ORDER BY impact_score DESC;
Fragmentation Scan — Current Database
-- Run in the target database; LIMITED mode is fast, DETAILED is accurate but slow
SELECT
OBJECT_NAME(ips.object_id) AS table_name,
i.name AS index_name,
ips.index_type_desc,
ips.avg_fragmentation_in_percent,
ips.page_count,
CASE
WHEN ips.avg_fragmentation_in_percent < 5 THEN 'No action'
WHEN ips.page_count < 100 THEN 'Ignore (small table)'
WHEN ips.avg_fragmentation_in_percent < 30 THEN 'REORGANIZE'
ELSE 'REBUILD'
END AS recommended_action
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
JOIN sys.indexes i ON i.object_id = ips.object_id AND i.index_id = ips.index_id
WHERE ips.index_type_desc <> 'HEAP'
AND ips.avg_fragmentation_in_percent > 5
ORDER BY ips.avg_fragmentation_in_percent DESC;
Top 25 Queries by CPU Cost
SELECT TOP 25
qs.total_worker_time / qs.execution_count AS avg_cpu_ms,
qs.total_logical_reads / qs.execution_count AS avg_reads,
qs.execution_count,
qs.total_worker_time AS total_cpu_ms,
SUBSTRING(st.text, (qs.statement_start_offset/2)+1,
((CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(st.text)
ELSE qs.statement_end_offset
END - qs.statement_start_offset)/2)+1) AS query_text,
qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
ORDER BY avg_cpu_ms DESC;
EXEC sp_configure or ALTER DATABASE statements ready to review and run.max degree of parallelism = 0 on a 32-core NUMA system). These should be addressed before any other tuning.sp_configure change, any ALTER DATABASE to a monitored setting, or any tempdb file count change.| Setting | Why It Matters | Common Misconfiguration |
|---|---|---|
max degree of parallelism | Controls CPU parallelism per query | 0 (unlimited) on NUMA systems causes CXPACKET storms |
cost threshold for parallelism | Minimum plan cost before parallel plan is used | Default value of 5 is too low for most OLTP workloads |
max server memory (MB) | Caps SQL Server buffer pool | Default 2,147,483,647 starves OS and other services |
tempdb file count | Reduces allocation page contention | Single file on multi-core servers causes GAM/SGAM waits |
optimize for ad hoc workloads | Reduces plan cache bloat from single-use plans | Disabled by default; should be ON for most OLTP systems |
backup compression default | Reduces backup I/O and storage | Disabled by default on Standard Edition |
Use these queries to read the current configuration and apply DPSMF-recommended values. Always test on a non-production instance first.
Read All Relevant sp_configure Settings
SELECT name, value_in_use AS current_value, description
FROM sys.configurations
WHERE name IN (
'max degree of parallelism',
'cost threshold for parallelism',
'max server memory (MB)',
'optimize for ad hoc workloads',
'backup compression default',
'remote admin connections',
'default trace enabled'
)
ORDER BY name;
MAXDOP — Calculate the Right Value for Your Hardware
-- Microsoft recommended MAXDOP formula
DECLARE @cpu_count INT = (SELECT cpu_count FROM sys.dm_os_sys_info);
DECLARE @numa_nodes INT = (SELECT COUNT(DISTINCT memory_node_id)
FROM sys.dm_os_memory_nodes
WHERE memory_node_id <> 64);
DECLARE @cores_per_numa INT = @cpu_count / NULLIF(@numa_nodes, 0);
SELECT
@cpu_count AS logical_cpus,
@numa_nodes AS numa_nodes,
@cores_per_numa AS cores_per_numa_node,
CASE
WHEN @numa_nodes = 1 AND @cpu_count <= 8 THEN @cpu_count -- single NUMA, ≤8 cores: use all
WHEN @numa_nodes = 1 AND @cpu_count > 8 THEN 8 -- single NUMA, >8 cores: cap at 8
WHEN @cores_per_numa <= 8 THEN @cores_per_numa -- multi-NUMA, ≤8/node: use node count
ELSE 8 -- multi-NUMA, >8/node: cap at 8
END AS recommended_maxdop;
-- Apply (requires sysadmin):
-- EXEC sp_configure 'max degree of parallelism', ;
-- RECONFIGURE;
Set Max Server Memory Safely
-- Leave 10% or 4 GB (whichever is larger) for the OS
DECLARE @total_ram_mb BIGINT = (SELECT physical_memory_kb / 1024 FROM sys.dm_os_sys_info);
DECLARE @reserve_mb BIGINT = CASE
WHEN @total_ram_mb * 0.10 > 4096 THEN CAST(@total_ram_mb * 0.10 AS BIGINT)
ELSE 4096
END;
DECLARE @recommended_mb BIGINT = @total_ram_mb - @reserve_mb;
SELECT
@total_ram_mb AS total_ram_mb,
@reserve_mb AS reserved_for_os_mb,
@recommended_mb AS recommended_max_server_memory_mb;
-- Apply:
-- EXEC sp_configure 'max server memory (MB)', ;
-- RECONFIGURE;
Tempdb File Count Check
-- Recommended: 1 file per logical CPU, max 8
DECLARE @logical_cpus INT = (SELECT cpu_count FROM sys.dm_os_sys_info);
DECLARE @tempdb_files INT = (SELECT COUNT(*) FROM tempdb.sys.database_files WHERE type = 0);
SELECT
@logical_cpus AS logical_cpus,
@tempdb_files AS current_tempdb_data_files,
CASE WHEN @logical_cpus > 8 THEN 8 ELSE @logical_cpus END AS recommended_files,
CASE WHEN @tempdb_files < CASE WHEN @logical_cpus > 8 THEN 8 ELSE @logical_cpus END
THEN 'ACTION REQUIRED: add tempdb data files'
ELSE 'OK'
END AS status;
-- To add a tempdb file (run in tempdb context):
-- ALTER DATABASE tempdb ADD FILE (
-- NAME = 'tempdev2',
-- FILENAME = 'D:\MSSQL\DATA\tempdb2.ndf',
-- SIZE = 512MB, FILEGROWTH = 64MB
-- );
msdb.dbo.backupset and suspect_pages across all monitored instances. Alerts when a database has not been backed up within its defined SLA window, or when a backup fails.01:00–05:00). Select days of week. During this window: no new alerts fire, anomaly scores are frozen, and performance metrics are excluded from baseline calculations.| Report | Cadence | Sections Included |
|---|---|---|
| Daily Health | Daily at chosen time | Alert count, top 3 performance issues, backup status, job failures, scorecard delta |
| Weekly Executive | Weekly (Mon 07:00 default) | Trend charts, scorecard history, top query changes, capacity forecast, action items |
| Monthly Trend | 1st of month | 30-day performance summary, index health evolution, security audit delta, growth projections |
| Security Audit | On-demand or scheduled | Full compliance scorecard, shadow identity list, permission map, TDE status, remediation checklist |
| Index Rebuild Plan | Weekly or on-demand | Fragmentation scan, rebuild/reorganise recommendations, estimated duration, maintenance window fit |
Run these queries in SSMS to verify backup coverage and SQL Agent job health — exactly what DPSMF monitors automatically.
Databases Missing a Recent Full Backup (SLA = 25 hours)
DECLARE @sla_hours INT = 25;
SELECT
d.name AS database_name,
d.recovery_model_desc,
MAX(b.backup_finish_date) AS last_full_backup,
DATEDIFF(HOUR, MAX(b.backup_finish_date), GETDATE()) AS hours_since_backup,
CASE
WHEN MAX(b.backup_finish_date) IS NULL THEN 'NEVER BACKED UP'
WHEN DATEDIFF(HOUR, MAX(b.backup_finish_date), GETDATE()) > @sla_hours
THEN 'SLA BREACHED'
ELSE 'OK'
END AS backup_status
FROM sys.databases d
LEFT JOIN msdb.dbo.backupset b
ON b.database_name = d.name
AND b.type = 'D' -- D = full database backup
WHERE d.database_id > 4 -- exclude system DBs
AND d.state_desc = 'ONLINE'
GROUP BY d.name, d.recovery_model_desc
ORDER BY hours_since_backup DESC;
Log Backup Gaps (Full Recovery Model Databases)
-- Flag databases in FULL recovery with no log backup in last 30 minutes
SELECT
d.name AS database_name,
MAX(b.backup_finish_date) AS last_log_backup,
DATEDIFF(MINUTE, MAX(b.backup_finish_date), GETDATE()) AS minutes_since_log_backup
FROM sys.databases d
LEFT JOIN msdb.dbo.backupset b
ON b.database_name = d.name
AND b.type = 'L' -- L = log backup
WHERE d.recovery_model_desc = 'FULL'
AND d.database_id > 4
AND d.state_desc = 'ONLINE'
GROUP BY d.name
HAVING MAX(b.backup_finish_date) IS NULL
OR DATEDIFF(MINUTE, MAX(b.backup_finish_date), GETDATE()) > 30
ORDER BY minutes_since_log_backup DESC;
SQL Agent Job History — Last 7 Days, Failures & Duration Regressions
WITH job_runs AS (
SELECT
j.name AS job_name,
h.run_date,
h.run_time,
h.run_status, -- 0=Fail 1=Succeed 2=Retry 3=Cancel
-- Convert HHMMSS integer to seconds
(h.run_duration / 10000) * 3600
+ ((h.run_duration % 10000) / 100) * 60
+ (h.run_duration % 100) AS duration_sec,
h.message
FROM msdb.dbo.sysjobhistory h
JOIN msdb.dbo.sysjobs j ON j.job_id = h.job_id
WHERE h.step_id = 0 -- step 0 = job-level outcome
AND msdb.dbo.agent_datetime(h.run_date, h.run_time) >= DATEADD(DAY,-7,GETDATE())
),
job_avg AS (
SELECT job_name,
AVG(duration_sec * 1.0) AS avg_duration_sec,
COUNT(*) AS run_count
FROM job_runs WHERE run_status = 1
GROUP BY job_name
)
SELECT
r.job_name,
msdb.dbo.agent_datetime(r.run_date, r.run_time) AS run_at,
CASE r.run_status WHEN 0 THEN 'FAILED' WHEN 1 THEN 'Succeeded'
WHEN 2 THEN 'Retry' WHEN 3 THEN 'Cancelled' END AS outcome,
r.duration_sec,
ROUND(a.avg_duration_sec,0) AS avg_duration_sec_30d,
CASE WHEN r.duration_sec > a.avg_duration_sec * 3
THEN 'DURATION REGRESSION' ELSE '' END AS regression_flag,
r.message
FROM job_runs r
LEFT JOIN job_avg a ON a.job_name = r.job_name
WHERE r.run_status <> 1 OR r.duration_sec > a.avg_duration_sec * 3
ORDER BY r.run_date DESC, r.run_time DESC;
dpsmf.ai/legal/dpa.sysadmin or any write permission. Create a dedicated low-privilege monitoring account.| Platform | Minimum Version | Notes |
|---|---|---|
| SQL Server | 2016 (13.x) | Full DMV support from 2016 onwards |
| Azure SQL Database | Any | Some DMVs restricted; DPSMF adapts automatically |
| Azure SQL Managed Instance | Any | Full support |
| Amazon RDS for SQL Server | SQL Server 2016+ | Windows Auth not available on RDS |
Sentinel is the primary monitoring view in DPSMF. It shows a live feed of all connected instances, current health scores, active alerts, and real-time metric sparklines — all updated every 10 seconds.
The SN1 live feed delivers a sub-second stream of metric events, alert state changes, and anomaly detections via server-sent events. It powers the real-time indicators on Sentinel tiles.
Individual Sentinel features (anomaly overlay, AG health panel, capacity widgets) can be enabled or disabled per-user by an admin under Settings → Sentinel Features. This allows feature rollout to be controlled without a code deployment.
| Category | Metrics | Type |
|---|---|---|
| CPU & Schedulers | SQL CPU %, OS CPU %, scheduler queue length, context switches | CPU |
| Memory | Buffer pool pages, page life expectancy, memory grants pending, stolen server memory | MEM |
| Wait Statistics | All 900+ wait types tracked; top 20 surfaced with baseline deviation | WAIT |
| I/O | Read/write latency per file, stall rate, pending I/O count | I/O |
| Blocking & Deadlocks | Active blocking chains, deadlock events, lock wait time, lead blocker query | LOCK |
| Query Performance | Top queries by CPU, duration, reads, writes, executions; plan regressions | QUERY |
| Index Health | Fragmentation %, missing index recommendations, unused indexes, duplicate indexes | INDEX |
| Tempdb | Version store size, allocation contention, active tasks per session | MEM |
| Connections | Active connections, blocked sessions, sleeping sessions, orphaned transactions | CONN |
| Data Type | Default Interval | Configurable |
|---|---|---|
| DMV snapshot (waits, CPU, blocking) | 10 seconds | 10s – 60s |
| Query stats (sys.dm_exec_query_stats) | 60 seconds | 30s – 300s |
| Index fragmentation scan | Daily (2 AM) | Schedule via Settings |
| Anomaly model refresh | Every 6 hours | Not configurable |
| Baseline recalculation | Daily (midnight) | Not configurable |
DPSMF collects instance-level metrics by default. Per-database monitoring extends this to track transaction log usage, index health, active query load, and alert thresholds individually per database — so a single busy reporting database does not mask the health of other databases on the same instance.
The AG Monitor tracks all Availability Groups on connected instances. It polls sys.dm_hadr_availability_group_states, sys.dm_hadr_database_replica_states, and sys.dm_hadr_availability_replica_states every 10 seconds.
Each alert opens a detail view showing the metric timeline, the anomaly score at the time of firing, correlated events within ±30 minutes, and AI-generated root cause analysis with a specific recommended action.
DPSMF enforces license-level policy limits. Monitor tier allows up to 10 active policies per instance. Intelligence and Enterprise tiers are unlimited. Policy checks are evaluated before every alert fires — a suppressed policy will not alert even if the condition is met.
A runbook is a structured set of steps that appears automatically when a specific alert type fires. Instead of DBA tribal knowledge living in someone's head, runbooks encode the correct response procedure and make it instantly available to any engineer on call.
DPSMF’s anomaly engine builds a rolling baseline for every tracked metric using a 7-day sliding window. It computes the mean and standard deviation for each metric at each time-of-day and day-of-week slot, then scores incoming values against the expected distribution.
| Score Range | Meaning | Action |
|---|---|---|
| 0 – 1σ | Normal variation | No alert |
| 1σ – 2σ | Elevated — watch | Logged only |
| 2σ – 3σ | Significant deviation | Info alert (Aggressive mode) |
| > 3σ | Strong anomaly | Warning or Critical alert |
| Mode | Alert Threshold | Best For |
|---|---|---|
| Conservative | 5σ | High-noise environments; batch/ETL servers |
| Default | 3σ | Most OLTP and mixed-workload environments |
| Aggressive | 2σ | Critical OLTP servers where early warning is paramount |
When an anomaly fires, DPSMF searches the previous 30 minutes for other anomalies on the same instance. Correlated events are ranked by temporal proximity and knowledge graph relationship strength, then presented in the alert detail view as likely contributing causes.
The knowledge graph is DPSMF’s long-term memory. Every query hash, wait type, metric event, and alert is stored as a node. Observed relationships between them — temporal co-occurrence, causal linkage, query-to-wait attribution — are stored as edges. A taxonomy layer classifies nodes into categories (workload, resource, maintenance, replication, etc.).
Open Knowledge Graph → Explorer (/kg-explorer.html) to browse nodes and edges interactively. Search by query hash, wait type name, metric name, or alert ID. Click any node to see its connected edges and the evidence strength for each relationship.
DPSMF fits a linear trend model to each capacity metric (CPU, buffer pool, data file size, log file size, connection count) using the last 30 days of collected data. It extrapolates forward and identifies the projected date when each metric will breach its defined capacity threshold.
| Dimension | Weight | What It Measures |
|---|---|---|
| Query Health | 25% | Top query baseline deviation, plan regression count, missing index severity |
| Wait Profile | 20% | Dominant wait types vs. baseline, unusual wait pattern frequency |
| Blocking | 15% | Active blocking chain count, max block duration, deadlock rate |
| I/O Performance | 15% | Read/write latency vs. baseline, stall rate |
| Memory Pressure | 10% | Page life expectancy trend, memory grants pending, buffer pool pressure |
| CPU Utilisation | 5% | Average and peak CPU vs. baseline, scheduler queue |
| Index Health | 5% | Fragmentation level, unused index count, missing index impact score |
| Connectivity | 5% | Connection count trend, orphaned transactions, sleeping session count |
Instances are added through a slide-in panel directly on the dashboard — no separate page required. The panel is always accessible from the top navigation bar.
| Setting | Default | Description |
|---|---|---|
| Poll Interval | 10s | How often DMVs are polled. Lower = more granular but higher overhead. |
| Alert Sensitivity | Default (3σ) | Anomaly threshold. Conservative / Default / Aggressive. |
| Baseline Window | 7 days | Rolling window used to compute the statistical baseline. |
| Maintenance Window | None | Time range when alerts are suppressed (e.g., nightly index rebuilds). |
| Tags | — | Free-text tags for grouping instances (e.g., Production, Staging, Region). |
Oracle ADB uses mTLS wallet-based authentication. No Oracle Instant Client is required — DPSMF connects in thin mode using the oracledb driver. Follow the steps below to connect an Always Free or paid ADB instance.
MyWallet2026!) and save the zip. You will need this password when adding the instance in DPSMF.ADMIN (or a least-privilege monitoring user)tnsnames.ora inside the wallet zip, e.g. dpsmf_tp. Use the _tp or _low alias for monitoring workloads.
.zip file onto the upload zone, or click Browse. Enter the Wallet Password you chose in step 1. DPSMF extracts ewallet.pem and tnsnames.ora automatically and stores them securely on the server.CREATE USER dpsmf_mon IDENTIFIED BY "..."; GRANT CREATE SESSION, SELECT ANY DICTIONARY TO dpsmf_mon; Then use dpsmf_mon as the username.A shadow user (shadow identity) is a dedicated SQL Server login that DPSMF uses for one specific category of operation on an instance. Instead of a single high-privilege service account that can do everything, you create up to five focused logins — one per function. Each login is granted only the permissions required for its role.
DPSMF stores the login name and (optionally) the credential for each shadow identity, validates that the login exists in sys.server_principals, and reads back the server permissions it has been granted. This gives you a live, audited map of exactly what each functional identity can see and do.
VIEW SERVER STATE only.ALTER SETTINGS at server level.VIEW ANY DEFINITION and VIEW SERVER STATE.db_owner on target databases.The traditional approach to monitoring is a single service account, often granted sysadmin or broad db_owner rights across every database. Shadow identities eliminate the four principal failure modes of that model:
| Risk | Single Account | Shadow Identities |
|---|---|---|
| Credential compromise | Attacker inherits all monitoring, tuning, config, maintenance, and security capabilities simultaneously | Compromised monitor login can only read DMVs — no write access, no schema visibility, no server config |
| Privilege escalation | A single over-privileged account is a natural pivot point for lateral movement | No single login holds both read and write permissions; escalation requires compromising independent accounts with no shared credential |
| Audit attribution | All DPSMF activity appears as one login in the SQL Server audit log — impossible to distinguish metric polling from index rebuilds | Each function uses a distinct login; the SQL Server audit log shows exactly which category performed which operation, at what time |
| Blast radius | A misconfiguration or bug in any DPSMF module can exploit the full permission set of the shared account | A bug in the tuner module is limited to ALTER on databases — it cannot touch server configuration or read security data |
DPSMF validates each shadow identity live against sys.server_principals and reads back its effective server permissions. This means misconfigured logins (e.g., missing VIEW SERVER STATE) are caught and surfaced in the interface before they silently fail during a collection cycle.
Run the T-SQL below on each SQL Server instance you want to shadow. You only need to create the logins for the categories you intend to use. Replace the placeholder passwords with strong, unique credentials.
-- ── MONITOR (metric collection — read-only) ──────────────────────────── CREATE LOGIN dpsmf_monitor WITH PASSWORD = 'MonitorPass2026!', CHECK_POLICY = ON; GRANT VIEW SERVER STATE TO dpsmf_monitor; GRANT VIEW ANY DEFINITION TO dpsmf_monitor; -- needed for index/query health -- ── TUNER (index + statistics work) ──────────────────────────────────── CREATE LOGIN dpsmf_tuner WITH PASSWORD = 'TunerPass2026!', CHECK_POLICY = ON; -- Grant ALTER on each target database, not server-wide: USE [YourDatabase]; CREATE USER dpsmf_tuner FOR LOGIN dpsmf_tuner; ALTER ROLE db_owner ADD MEMBER dpsmf_tuner; -- or a custom role with ALTER INDEX -- ── CONFIG (server-level settings via sp_configure) ───────────────────── CREATE LOGIN dpsmf_config WITH PASSWORD = 'ConfigPass2026!', CHECK_POLICY = ON; GRANT ALTER SETTINGS TO dpsmf_config; GRANT VIEW SERVER STATE TO dpsmf_config; -- ── SECURITY (audit login hygiene and permission maps) ─────────────────── CREATE LOGIN dpsmf_security WITH PASSWORD = 'SecurityPass2026!', CHECK_POLICY = ON; GRANT VIEW ANY DEFINITION TO dpsmf_security; GRANT VIEW SERVER STATE TO dpsmf_security; -- ── MAINTENANCE (DBCC, integrity, cleanup) ────────────────────────────── CREATE LOGIN dpsmf_maint WITH PASSWORD = 'MaintPass2026!', CHECK_POLICY = ON; -- Grant db_owner (or custom maintenance role) per database: USE [YourDatabase]; CREATE USER dpsmf_maint FOR LOGIN dpsmf_maint; ALTER ROLE db_owner ADD MEMBER dpsmf_maint;
monitor, tuner, config, security, or maintenance). Enter the SQL Login name exactly as created on the SQL Server (e.g. dpsmf_monitor). Optionally enter the Password — required if DPSMF will use this identity to connect actively, not needed for audit-only registration.sys.server_principals to confirm the login exists and is enabled, then reads its effective permissions from sys.server_permissions. The badge updates to Validated ✓ with a permissions summary, or shows a specific error if the login is missing or disabled.sys.server_principals. The shadow credential is only used when DPSMF actively connects as that identity (e.g., the tuner running an index rebuild).| Category | SQL Login | Minimum Required Permissions | What DPSMF Uses It For |
|---|---|---|---|
monitor | dpsmf_monitor | VIEW SERVER STATE, VIEW ANY DEFINITION | DMV polling, wait stats, query stats, index fragmentation, session counts |
tuner | dpsmf_tuner | ALTER INDEX on target DBs, or db_owner per database | UPDATE STATISTICS, index rebuild / reorganize recommendations, sp_recompile |
config | dpsmf_config | ALTER SETTINGS, VIEW SERVER STATE | Reading and applying sp_configure settings, max memory, MAXDOP, cost threshold |
security | dpsmf_security | VIEW ANY DEFINITION, VIEW SERVER STATE | Login audit, permission maps, SQL auth hygiene scoring, drift detection |
maintenance | dpsmf_maint | db_owner on target databases (or custom role with DBCC permissions) | DBCC CHECKDB, DBCC SHRINKFILE, integrity checks, log space reclaim |
^[A-Za-z0-9_\-.\$\\]{1,128}$ before storage. Names containing SQL injection characters, spaces, or path traversal sequences are rejected by the API regardless of what is typed in the UI.monitor category is required for metric collection on SQL Server instances. The other four are optional and only come into play when you use those specific DPSMF features (tuning recommendations, server config management, security audit, or scheduled maintenance). Register them as you enable each capability.sys.server_principals to confirm the shadow login exists and is not disabled, then queries sys.server_permissions to retrieve the permission set. The result is stored as permissions_detected on the shadow identity record — visible in the UI alongside the validation timestamp. Validation does not use or test the shadow identity's own password.monitor and tuner, it must hold the combined permission set of both, which means a compromise of either function gains access to both. Use distinct logins with distinct passwords for each category.sys.server_principals but is missing one of the required grants. Check the permissions_detected field shown after validation — it lists the exact server permissions DPSMF found. Compare them against the Permissions Reference table above and grant any that are missing.mydb_low, mydb_tp). Use _low or _tp for monitoring.MyWallet2026!) and save the .zip file. Do not unzip it — DPSMF handles that.tnsnames.ora. It lists 5 aliases: high, medium, low, tp, tpurgent — all prefixed with your DB name. Use the _tp alias for DPSMF monitoring (transaction processing profile, low priority).ADMIN), Password (your ADB ADMIN password), TNS Connect String (e.g. mydb_tp). Drop the wallet zip on the upload zone and enter the wallet password. Click Add Instance.| Metric | Source View | Notes |
|---|---|---|
| CPU Busy % | V$OSSTAT | BUSY_TIME / ELAPSED_TIME ratio across all CPUs |
| SGA Memory (MB) | V$SGAINFO | Total SGA, buffer cache, redo buffers |
| PGA Memory (MB) | V$PGASTAT | Total PGA allocated and in-use |
| Sessions | V$SESSION | Total / active / inactive / blocked user sessions |
| Wait Classes | V$SYSTEM_WAIT_CLASS | Top 10 non-idle wait classes by cumulative time |
| Schema Sizes | DBA_SEGMENTS | Object count and MB per user schema (excludes Oracle internals) |
| Index Health | DBA_INDEXES + DBA_IND_STATISTICS | Status, B-level depth, row count; flags high-blevel indexes |
| Security Posture | DBA_USERS, DBA_SYS_PRIVS | Locked/expired accounts, DBA grantees, PUBLIC privileges |
AWR and DBA_HIST_* views require the Oracle Diagnostics Pack licence and are not queried by DPSMF on Always Free ADB. All metrics above use base V$ and DBA_* views available without additional licensing.Using ADMIN for monitoring works but gives DPSMF full DBA access. For production use, create a dedicated read-only monitoring user:
-- Run as ADMIN in SQL*Plus, SQLcl, or OCI Database Actions CREATE USER dpsmf_mon IDENTIFIED BY "MonitorPass2026!"; GRANT CREATE SESSION TO dpsmf_mon; GRANT SELECT ANY DICTIONARY TO dpsmf_mon; GRANT SELECT ON V_$SESSION TO dpsmf_mon; GRANT SELECT ON V_$OSSTAT TO dpsmf_mon; GRANT SELECT ON V_$SGAINFO TO dpsmf_mon; GRANT SELECT ON V_$PGASTAT TO dpsmf_mon; GRANT SELECT ON V_$SYSTEM_WAIT_CLASS TO dpsmf_mon;
Then use dpsmf_mon as the Username and its password when adding the instance in DPSMF.
| Role | Capabilities |
|---|---|
| dba | Full access: acknowledge/resolve alerts, manage instances, runbooks, policies, settings |
| viewer | Read-only: view dashboards, alerts, metrics, and scorecard — no write actions |
| readonly | Demo-level: GET requests only — suitable for stakeholder dashboards |
Go to Settings → Users, find the user, and click Reset Password. Enter a new temporary password (min 8 characters). The user will be required to change it on next login.
| Tier | Instances | Anomaly | AG Monitor | Capacity | Policies |
|---|---|---|---|---|---|
| Monitor | 3 | ✓ | ✗ | ✗ | 10/instance |
| Intelligence | 10 | ✓ | ✓ | ✓ | Unlimited |
| Enterprise | Unlimited | ✓ | ✓ | ✓ | Unlimited |
Go to Settings → License → Activate and enter your license key. Keys take the format DPSMF-XXXXXX-XXXXXX. After activation, the license manager shows the tier, expiry date, instance count, and days remaining.
| Section | Key Settings |
|---|---|
| General | Default alert sensitivity, timezone, date format, dashboard refresh rate |
| Instances | Add / edit / remove monitored instances; per-instance poll interval and maintenance windows |
| Notifications | Alert email recipients, escalation contacts (L1/L2), email server configuration |
| Policies | Create and manage alert policies; set suppression windows |
| Runbooks | Create and manage remediation runbooks attached to alert types |
| Users | Add users, assign roles, reset passwords |
| License | View current license, activate new keys, check usage vs. limits |
| Reports | Configure scheduled daily/weekly email reports |
| Sentinel Features | Enable/disable individual dashboard feature panels per user |
DPSMF does not require you to manually describe your topology. When the collector agent connects to a SQL Server instance for the first time, it automatically discovers and reports:
This information is sent on the first push and kept current on every subsequent push. The Global Dashboard populates automatically — no manual configuration of topology is required.
The current deployment model is one collector agent per SQL Server instance. Each agent runs as a Windows service on the host machine (or any machine with network access to port 1433) and pushes metrics to DPSMF every 60 seconds.
| Environment | Agents Needed | Notes |
|---|---|---|
| Single SQL Server | 1 | Install on the SQL Server host or any machine with network access |
| Multiple SQL Servers | 1 per instance | Each agent has its own .env pointing to its target instance |
| Always On AG (primary + secondaries) | 1 per replica | Connect all replicas so DPSMF can show cross-replica health |
| Named instances on shared host | 1 per named instance | Each named instance gets its own agent with its own port |
.env, then install as a Windows Service:# 1. Download and extract dpsmf-agent-v2.zip from dpsmf.ai/dpsmf/downloads/ # 2. Edit .env with your SQL Server credentials npm install node dpsmf-agent.js # test — verify Push OK in output node dpsmf-agent.js --install # register as Windows Service (auto-starts on reboot)
The next evolution of DPSMF deployment is a single discovery agent that scans a network segment, identifies all SQL Server instances automatically, and registers them with the dashboard — no per-instance configuration required.
Planned capabilities:
| Item | Automatic | Manual |
|---|---|---|
| Server name, version, edition | ✓ Auto-detected on first push | |
| Database inventory | ✓ Auto-detected on every push | |
| CPU, memory, and hardware profile | ✓ Auto-detected on first push | |
| Performance metrics (CPU, waits, sessions) | ✓ Pushed every 60 seconds | |
| Agent SQL credentials | Set once in .env file | |
| Alert thresholds and policies | Configured in the dashboard | |
| AG replica relationships (current) | Connect each replica as a separate instance |
DPSMF Enterprise supports SAML 2.0, compatible with Okta, Azure Active Directory, Google Workspace, and any SAML 2.0-compliant identity provider. Once configured, users authenticate through your IdP — DPSMF issues a session token and never handles passwords directly.
https://your-org.okta.com/app/metadata). DPSMF fetches the certificate and endpoint URLs automatically.dba-team → DPSMF role dba.DPSMF has two layers of access control. Global roles (Viewer, Analyst, DBA, Admin) set what actions a user can perform. Instance scoping controls which connected SQL Server instances they can see. The two layers combine: a DBA scoped to Instance A cannot see Instance B, even if they have full DBA permissions globally.
Scoping is enforced at the API layer — not just the UI. A scoped user cannot retrieve data for out-of-scope instances via direct API calls.
Global thresholds apply to every connected instance. In most enterprise environments that causes two problems: production alerts are missed because thresholds are tuned for dev, or dev instances generate constant noise because they are held to production standards.
Per-instance overrides let you set a PLE critical threshold of 300 on production and a PLE warning threshold of 50 on a dev box — each instance is judged against its own context.
Every significant action in DPSMF writes a row to the audit log: logins, logouts, failed login attempts, user creation and modification, role changes, instance connections and disconnections, alert rule changes, Sentinel AI queries, and configuration changes. Each row records the user, timestamp, action type, affected resource, and originating IP address.
The status page at dpsmf.ai/status shows current health and 90-day uptime history for each DPSMF service component: the Collector engine, the API layer, Sentinel AI, the Knowledge Graph engine, and the web application. No login is required — share the URL with your IT team or include it in vendor questionnaires.
Enterprise customers can subscribe to status notifications directly from the status page. Delivery options: email, SMS, or webhook. Notifications fire on any status change — component degradation, incident open, incident resolved, and maintenance start/end.