> ## Documentation Index
> Fetch the complete documentation index at: https://docs.curator.interworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Windows - Investigating CPU Spikes

> How to use the CPU investigation script to find out what a Windows Curator server was doing when its CPU spiked, and how to capture the next spike.

When a Windows Curator server hits 100% CPU, the spike is usually over before anyone can look at the server. Windows
keeps no CPU history on its own. The only way to explain a spike after the fact is to line up the records the server
does keep: the Apache access and error logs, the PHP and Curator logs, the Windows event logs, the Task Scheduler
history, and any Performance Monitor logs.

The CPU investigation script lines those records up for you. It reads each source and builds one timeline in 5-minute
buckets. The report names the busiest periods, lists what else was happening at the same time, and ranks the
configuration problems it found by severity. Two more modes capture the next spike as it happens.

<Note>
  The script is for self-hosted Curator installations on Windows Server that use the bundled Apache web server. It reads
  the server; it does not change anything unless you ask it to register a Performance Monitor collector. Nothing is
  uploaded anywhere. The report is written to the Desktop for you to review and send to the Curator support team.
</Note>

## Requirements

* Windows Server 2016, 2019 or 2022 with Windows PowerShell 5.1 (installed by default).
* A PowerShell session started with **Run as administrator**. Without it, the Task Scheduler history, some event logs
  and the Windows Defender settings cannot be read, and the report says so in its "Collection problems" section.
* Enough time: the first run reads every access log in the window. A busy server with 14 days of logs takes a few
  minutes.

## Step 1: Run the historical analysis

Open an elevated PowerShell window on the Curator server and run:

```powershell theme={null}
Invoke-WebRequest -Uri "https://api.curator.interworks.com/scripts/curator_cpu_investigation.ps1" -OutFile "$env:TEMP\curator_cpu_investigation.ps1"
PowerShell -ExecutionPolicy Bypass -File "$env:TEMP\curator_cpu_investigation.ps1" -Days 14
```

The script finds the Curator installation on its own (any drive, any folder name), reads the Apache configuration to
learn where the access logs are and what format they use, and then works through each source. Progress prints to the
console as it goes.

When it finishes, two files are on the Desktop of the account that ran it:

| File                                           | Contents                                                       |
| ---------------------------------------------- | -------------------------------------------------------------- |
| `curator_cpu_history_<server>_<timestamp>.md`  | The report. Open it in any Markdown viewer or a text editor.   |
| `curator_cpu_history_<server>_<timestamp>.csv` | One row per 5-minute bucket, for charting in Excel or Tableau. |

Use `-OutputDir` to write the files somewhere other than the Desktop.

## Step 2: Read the report

The report opens with a **Findings** block, ranked HIGH, MEDIUM, LOW and INFO. HIGH findings are known CPU sinks on
Windows Curator servers and are worth fixing whether or not they explain the spikes. The most common ones are:

* **OPcache is not enabled.** Without it, every request compiles Curator's PHP from source again. Apply the
  [recommended OPcache settings](https://api.curator.interworks.com/assets/WindowsWorkingOpcacheSettings.txt) and
  restart Apache.
* **Windows Defender is scanning the Curator folder.** Real-time scanning of every PHP file, cache file and database
  page write shows up as `MsMpEng.exe` using the CPU and Apache looking slow. Exclude the Curator folder and the
  `httpd.exe`, `php.exe` and `mariadbd.exe` processes.
* **Scheduled task runs are taking longer than a minute.** Curator's scheduled task runs `artisan schedule:run` every
  minute and is meant to finish well inside that. Long runs mean a scheduled job (an export, a cache warm, a Tableau
  sync) is doing heavy work in the foreground, and the report lists the longest runs so you can line them up with the
  spikes.
* **Monitoring or bot traffic is a large share of requests.** A health check that hits a Curator page instead of a
  static file runs the full application on every poll.

Below the findings, the **Busiest periods** section lists the buckets with the most dynamic (PHP) requests, ranked by
how far above the average they are. Each bucket shows the sites, top paths and top client addresses that made up the
traffic, plus every event, error and task run that happened within a few minutes of it. Compare the times you were
told about with this list:

* If the reported spike times line up with a traffic peak, the CPU is going to users. Look at the top paths and clients
  in that bucket, and at the findings that make each request cheaper.
* If they do not line up with traffic, the CPU is going to background work. Look at the task runs, Defender scans,
  Windows Update installs and errors listed next to the nearest buckets.

The **CPU history** section is empty on a first run unless the server already had Performance Monitor logs, because
Windows records no CPU history by default. The next step fixes that.

<Note>
  The report also lists any source the script could not read in full under **Collection problems**. An empty section for
  one of those sources means "could not look", not "nothing happened". Re-run from an elevated PowerShell if the list
  mentions access or permissions.
</Note>

## Step 3: Capture the next spike

Register a lightweight Performance Monitor collector so the next spike is recorded with per-process CPU:

```powershell theme={null}
PowerShell -ExecutionPolicy Bypass -File "$env:TEMP\curator_cpu_investigation.ps1" -InstallPerfLog
```

The collector samples total CPU and the CPU of `httpd.exe`, `php.exe`, `mariadbd.exe`, `MsMpEng.exe` and a few other
processes every 15 seconds into a 512 MB circular file under `C:\PerfLogs\Curator`. A scheduled task restarts it after
a reboot. The overhead is negligible.

After the next spike, run Step 1 again. The report will now include a **CPU history** section with the highest CPU
buckets, the top process in each, and a correlation figure between traffic and CPU that says whether the spikes come
from users or from background work.

When the investigation is over, remove the collector:

```powershell theme={null}
PowerShell -ExecutionPolicy Bypass -File "$env:TEMP\curator_cpu_investigation.ps1" -RemovePerfLog
```

The recorded `.blg` files are left in place so they can still be analyzed.

## Step 4: Watch a spike live

If a spike is happening now, or is expected at a known time, sample the server live:

```powershell theme={null}
PowerShell -ExecutionPolicy Bypass -File "$env:TEMP\curator_cpu_investigation.ps1" -Watch -WatchMinutes 60 -CpuThreshold 80
```

The script samples CPU every 5 seconds and prints the top processes each time. Whenever total CPU crosses the
threshold, it records a snapshot: the top processes, the command lines of the `php.exe` processes (which shows which
`artisan` command is running), the number of open web connections, and the queries MariaDB is executing. Press
**Ctrl+C** to stop early. The report and a CSV of every sample are written to the Desktop either way.

## Parameters

| Parameter         | Default  | Purpose                                                                 |
| ----------------- | -------- | ----------------------------------------------------------------------- |
| `-Days`           | 7        | How many days back the historical analysis covers.                      |
| `-BucketMinutes`  | 5        | Timeline resolution. Use 1 for a sharper look at a known spike window.  |
| `-CuratorPath`    | detected | Curator installation folder, if the script cannot find it.              |
| `-AccessLog`      |          | Extra access log files or wildcards to include.                         |
| `-PerfLogPath`    |          | Extra Performance Monitor `.blg` or `.csv` files or folders to include. |
| `-OutputDir`      | Desktop  | Where to write the report and CSV.                                      |
| `-Watch`          |          | Sample live instead of analyzing history.                               |
| `-WatchMinutes`   | 30       | How long to watch.                                                      |
| `-CpuThreshold`   | 80       | CPU percentage that triggers a snapshot while watching.                 |
| `-SampleSeconds`  | 5        | Seconds between samples while watching.                                 |
| `-InstallPerfLog` |          | Register and start the Performance Monitor collector.                   |
| `-RemovePerfLog`  |          | Stop and delete the collector.                                          |

## What the report contains

The report is internal material. It contains hostnames, file paths, client IP addresses and request paths from the
access logs, and messages from the error logs. A `-Watch` snapshot adds process command lines and the shape of the
running database queries. Before the script writes any of those, it strips query strings from URLs, replaces
credential-looking values, and replaces the literal values in SQL statements with placeholders.

The database credentials in each site's `.env` file are used only to run read-only status queries. The password is
never written to the report, the console or a command line.

## Send the report for analysis

Email the report and the CSV to the Curator support team, together with the files described in
[Performance Diagnostics](/site_administration/performance/performance_diagnostics), and say which spike times you were
told about. The support team will read the timeline against those times and recommend specific changes.

## Troubleshooting

* **"No Apache access logs were found."** The access log is declared by a `CustomLog` line in `curator.conf` or
  `httpd.conf`. If logging was turned off, turn it back on and re-run after a day of traffic.
* **"Task Scheduler history is disabled on this server."** Open Task Scheduler and choose **Enable All Tasks History**
  in the Actions pane, or run `wevtutil sl Microsoft-Windows-TaskScheduler/Operational /e:true` from an elevated
  prompt. The next run can then measure how long each `schedule:run` takes.
* **"The access log format has no request duration field."** Adding `%D` to the `LogFormat` line in `curator.conf`
  lets the next run report how long each request took, which separates slow pages from busy ones. Restart Apache after
  the change.
* **The CPU history section is empty.** Windows keeps no CPU history unless a collector is running. Follow Step 3 and
  re-run after the next spike.
