Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
800 commits
Select commit Hold shift + click to select a range
9df7e0c
Guard job runs actionlint on changed workflow files; pure string pins…
erikdarlingdata Oct 16, 2026
18f38ba
WebServerPageRangeTests: the past-end poll test no longer misses a re…
erikdarlingdata Oct 16, 2026
e00b617
Tests: the CI skip marker is counted apart from failures, two wait he…
erikdarlingdata Oct 16, 2026
8400817
DuckDbInvestigationStoreTests: the report-links test no longer races …
erikdarlingdata Oct 16, 2026
b1ef9f9
The 24-hour Query Store top read sizes its time floor to the server's…
erikdarlingdata Oct 16, 2026
779693d
PR CI builds the Darling and Lite test projects in parallel jobs (#59…
erikdarlingdata Oct 16, 2026
dd0033a
Ring-buffer collectors escape stray ampersands before the XML cast, a…
erikdarlingdata Oct 16, 2026
7404409
CI quarantine list: a test dev's latest push failed is skipped on PRs…
erikdarlingdata Oct 16, 2026
09c7631
Count pins that fail correct changes: ladder, MCP roster and DI pins …
erikdarlingdata Oct 16, 2026
3e68e0b
Derive the Reads=Lite and Reads=Darling class sets from the source sc…
erikdarlingdata Oct 16, 2026
3e8a594
Run all collectors now, or one collector now, for a server from the w…
erikdarlingdata Oct 16, 2026
c12233c
Lint the workflow shell scripts with actionlint and shellcheck on ubu…
erikdarlingdata Oct 16, 2026
2098669
Client Timeouts: the attention-rate measurement for the field week (#…
erikdarlingdata Oct 16, 2026
d82cab4
Query tags: the shared rule matcher and sighting record (#5979) (#5995)
erikdarlingdata Oct 16, 2026
6da8c88
Share the tag tree rules between Darling and Lite in PerformanceMonit…
erikdarlingdata Oct 16, 2026
2bf33b2
Test selection runs everything for a shared-project change only when …
erikdarlingdata Oct 16, 2026
768a8c0
Query tags: the store tables, grants, server removal and retention (#…
erikdarlingdata Oct 16, 2026
9957ee4
Investigation briefs say how often the alert fired at this time of da…
erikdarlingdata Oct 16, 2026
e747360
Investigation requests, briefs and webhook tokens carry both server n…
erikdarlingdata Oct 16, 2026
b4c15c4
Query tags: collectors describe the query keys and caller fields each…
erikdarlingdata Oct 17, 2026
b827f7e
Query tags: Darling MCP tools to create, edit, delete, list and test …
erikdarlingdata Oct 17, 2026
69ea8e8
Light review tier: max turns 30 -> 100 (#5683)
erikdarlingdata Oct 17, 2026
3b2afdf
Phase C store: the one migration, grants and retention (#5997) (#6017)
erikdarlingdata Oct 17, 2026
c36d958
Query tags in Lite: the DuckDB tables and the tag, assignment and rul…
erikdarlingdata Oct 17, 2026
c572445
Query tags: Darling query reads carry the tag and filter by it (#5984…
erikdarlingdata Oct 17, 2026
03cb75c
Query tags: Darling MCP tools to create, edit, delete, assign and lis…
erikdarlingdata Oct 17, 2026
2cb9eed
Investigation reports carry a family, fix links, evidence basis and t…
erikdarlingdata Oct 17, 2026
a5d2826
Query tags: the Darling tag map and sightings store (#5985) (#6025)
erikdarlingdata Oct 17, 2026
6512daa
Investigation evidence capture: frozen store rows per section with co…
erikdarlingdata Oct 17, 2026
49bed41
Query tags in Lite: sightings with each batch and the tag map each pa…
erikdarlingdata Oct 17, 2026
e2a17f8
Query tags: get_resource_use_by_tag rolls resource use up by tag in D…
erikdarlingdata Oct 17, 2026
adc6b31
Signed generic webhooks: sealed keys, Standard Webhooks signatures, 7…
erikdarlingdata Oct 17, 2026
f31c711
Query tags in Lite: get_resource_use_by_tag and tags on the query rea…
erikdarlingdata Oct 17, 2026
e7a78a1
Lock the self-alert test harness's outcome list so a poll never sees …
erikdarlingdata Oct 17, 2026
3a8dbb3
Investigation evidence: deadlock, blocking and regression sections (#…
erikdarlingdata Oct 17, 2026
d5cceaf
Query tags in Lite: MCP tools to manage tags, assignments and rules (…
erikdarlingdata Oct 17, 2026
329ce15
Query tags in Lite: the manage window, the tag column and filter, and…
erikdarlingdata Oct 17, 2026
5762b9e
Request events reach the agent route, fall back when unclaimed, and w…
erikdarlingdata Oct 17, 2026
a73b08d
Review prompt asks for behaviour coverage, not pins (#5833) (#5836)
erikdarlingdata Oct 17, 2026
a6de0b8
Add get_investigation_evidence, a claimed request's frozen evidence r…
erikdarlingdata Oct 17, 2026
e61027d
Query tag assign/unassign folds case like the resolver (#6027) (#6039)
erikdarlingdata Oct 17, 2026
f3ec468
Set a notification route's fallback route from the viewer and MCP (#6…
erikdarlingdata Oct 17, 2026
339b75a
Query tags: the Darling viewer's tag column, filter and by-tag view (…
erikdarlingdata Oct 17, 2026
597a946
Skip the capture-down store read for PostgreSQL targets (#6042) (#6045)
erikdarlingdata Oct 17, 2026
f437f00
Run the module map repair once a day, not after every purge pass (#60…
erikdarlingdata Oct 17, 2026
aad139b
Index the PostgreSQL statement-text due check by server and last_seen…
erikdarlingdata Oct 17, 2026
04443a7
Query tags: web by-tag page, and a tag column and filter on the query…
erikdarlingdata Oct 17, 2026
abd890e
Query tags: web page to manage query tags, assignments and rules (#59…
erikdarlingdata Oct 17, 2026
51245f7
Investigation long-poll live test waits for the poll to park, not onl…
erikdarlingdata Oct 17, 2026
964e2de
Investigation briefs hint at a plan change behind high CPU, and the r…
erikdarlingdata Oct 17, 2026
74bc02d
Investigation reports can schedule a follow-up request (#6008) (#6053)
erikdarlingdata Oct 17, 2026
c6a6dd5
Lite investigations get frozen evidence, sections and request events …
erikdarlingdata Oct 18, 2026
986991f
Deadlocks get a signature, a daily rollup, an agent gate and a digest…
erikdarlingdata Oct 18, 2026
aa6df1a
Investigation follow-ups can wait for a module deploy (#6009) (#6056)
erikdarlingdata Oct 18, 2026
ed8180b
Deploy follow-ups match module names in any case, and the module map …
erikdarlingdata Oct 18, 2026
e8955cd
Lite investigation requests journal a quiet join and wait for every m…
erikdarlingdata Oct 18, 2026
0355047
An agent can merge two deadlock signatures, journaled and undoable (#…
erikdarlingdata Oct 18, 2026
1dcaa72
Lite investigations carry server names, time-of-day recurrence and th…
erikdarlingdata Oct 18, 2026
a4be484
The query-tag rule list names servers and filters by server, and the …
erikdarlingdata Oct 18, 2026
3a46a9d
Lite opens time follow-ups and signs generic webhooks (#6012) (#6064)
erikdarlingdata Oct 18, 2026
a24072b
An MCP read checks a Query Store plan before anyone forces it (#5632)…
erikdarlingdata Oct 18, 2026
b025214
Docs: the MCP tool counts agree with the code, and the support docs s…
erikdarlingdata Oct 18, 2026
1c85477
The force pre-check blocks only a force that cannot work, and warns a…
erikdarlingdata Oct 18, 2026
da7cf88
Alerts keep joining a request while they keep firing, up to 4 hours (…
erikdarlingdata Oct 18, 2026
a18866a
--backfill-rollups runs on Linux when the store credential is not DPA…
erikdarlingdata Oct 18, 2026
a9c11c0
Darling collects each database's forced plans every 5 minutes (#5632)…
erikdarlingdata Oct 18, 2026
010ee11
The day-grain Query Store daily fills again, and retention on its par…
erikdarlingdata Oct 18, 2026
3bc149c
Alert history filters, find_database, a read-only Query Store hint an…
erikdarlingdata Oct 18, 2026
f441e92
Darling ranks queries by aborted and failed executions (#6073) (#6078)
erikdarlingdata Oct 18, 2026
04e0695
Level 3 routes and per-server action switches, all off (#5632) (#6080)
erikdarlingdata Oct 19, 2026
8eaf994
Darling says whether each forced plan works (#5632) (#6079)
erikdarlingdata Oct 19, 2026
2ab3a83
Darling fills a small rollup floor gap by itself (#6081) (#6082)
erikdarlingdata Oct 19, 2026
83ff5b1
The server write code in its own assembly, uncalled and off (#5632) (…
erikdarlingdata Oct 19, 2026
c159aae
Darling says who drove the load: CPU by database, load by caller, sam…
erikdarlingdata Oct 19, 2026
657873e
Agents can force and unforce a Query Store plan, behind the server ac…
erikdarlingdata Oct 19, 2026
66684d5
After an agent forces a plan, Darling opens timed checks for the agen…
erikdarlingdata Oct 19, 2026
0a30083
A person can undo an agent's plan force or unforce from the Agent cha…
erikdarlingdata Oct 19, 2026
5bc9077
A login or host gets the same pseudonym in every metadataOnly answer …
erikdarlingdata Oct 19, 2026
b6a0583
An agent can evict one cached plan and then see its next compile (#56…
erikdarlingdata Oct 19, 2026
c0be374
The guard stage runs the source scans that CI ran only in the Postgre…
erikdarlingdata Oct 19, 2026
6c11193
The blocking capture records the head blocker's sign-in time, idle ti…
erikdarlingdata Oct 19, 2026
618bcb2
Bump Microsoft.CodeAnalysis.BannedApiAnalyzers from 4.14.0 to 5.6.0 (…
dependabot[bot] Oct 19, 2026
ca8963d
Bump dotnet-sdk from 10.0.401 to 10.0.402 (#6090)
dependabot[bot] Oct 19, 2026
a9e6380
Point a viewer-only machine at --export-viewer-config when darling.js…
erikdarlingdata Oct 19, 2026
6a21b67
Job History: sort Duration, Step and Retries by their numbers and sho…
erikdarlingdata Oct 19, 2026
a76bc96
Give the Query Store text fetch a per-database time budget (#6102) (#…
erikdarlingdata Oct 19, 2026
8434419
Sort the PostgreSQL grids' size, duration, age, count and percent col…
erikdarlingdata Oct 19, 2026
eba4d18
An agent can kill one SQL Server session after checking it is still t…
erikdarlingdata Oct 19, 2026
03a9339
Add a fleet-wide excluded volumes list to the Volume Free Space alert…
erikdarlingdata Oct 19, 2026
d827bce
Bump AWSSDK.PI and 7 others (#6107)
dependabot[bot] Oct 19, 2026
bbda087
Let operators set the order of sibling server tags (#6111)
erikdarlingdata Oct 19, 2026
2becf7e
Compose race test counts only its own database's advisory lock waits …
erikdarlingdata Oct 19, 2026
1843cc5
Excluded databases and volumes boxes take one entry per line, so a da…
erikdarlingdata Oct 19, 2026
7431aa3
Two tests stop failing on state other tests leave behind (#6113) (#6116)
erikdarlingdata Oct 19, 2026
8aa88a7
An agent's server change is journaled as done when the store's commit…
erikdarlingdata Oct 19, 2026
2e0e5b8
Lite gets get_cpu_by_database, get_load_by_caller and get_query_pileu…
erikdarlingdata Oct 19, 2026
b62b2e5
Ship the Compose files as a release asset and say which Linux downloa…
erikdarlingdata Oct 19, 2026
0de98df
get_top_queries_by_cpu says when a window stored snapshots but none c…
erikdarlingdata Oct 19, 2026
40411c5
PostgreSQL MCP reads use the documented status words, lead with offli…
erikdarlingdata Oct 19, 2026
c121366
get_memory_grants: a zero window peak has no time, and the answer say…
erikdarlingdata Oct 19, 2026
973ac6f
PostgreSQL tabs: Dead % header, human index sizes, display name in no…
erikdarlingdata Oct 19, 2026
a78623b
A PostgreSQL server's Vacuum tab scrolls and every grid shows several…
erikdarlingdata Oct 19, 2026
5266a59
The load-test measurement script reads the real service log and takes…
erikdarlingdata Oct 19, 2026
3a7b2a1
Lite shard step: name both sides of a mismatch, retry the cut once, r…
erikdarlingdata Oct 19, 2026
8267b5b
get_query_store_regressions: regression_count is the total that passe…
erikdarlingdata Oct 19, 2026
7c505e6
analyze_server: no recurrence label on settings findings, and say whe…
erikdarlingdata Oct 19, 2026
a908b52
Viewer: a PostgreSQL finding's link opens the PostgreSQL Activity tab…
erikdarlingdata Oct 19, 2026
a1452af
Name servers by display name on the Availability Groups and Query Tag…
erikdarlingdata Oct 19, 2026
9d5c61f
Show cut-off text in full in Job History, Alert History, Settings, Ma…
erikdarlingdata Oct 19, 2026
fd98612
A default Linux compose install needs one setup command, and MCP off …
erikdarlingdata Oct 19, 2026
b93c134
Web charts: no repeated axis labels, room for wide labels, and a whol…
erikdarlingdata Oct 19, 2026
e2a4cbf
PostgreSQL grids on the web server page keep short values in their ce…
erikdarlingdata Oct 19, 2026
3eec998
Show the head blocker's login time, last request end, open transactio…
erikdarlingdata Oct 20, 2026
095e221
kill_session ends one PostgreSQL session after checking it is still t…
erikdarlingdata Oct 20, 2026
1ee47c6
get_forced_plans counts a recompiled force as the force, and names a …
erikdarlingdata Oct 20, 2026
ba9126b
Web page text reads plainly: short banners, no capitals for emphasis,…
erikdarlingdata Oct 20, 2026
f3a7e64
Two UI tests no longer depend on which classes ran before them (#6175…
erikdarlingdata Oct 20, 2026
c02e933
The procedure_stats deferred plan fetch live tests no longer depend o…
erikdarlingdata Oct 20, 2026
1384cf2
Lists that mix servers read every row on one clock (#6137) (#6161)
erikdarlingdata Oct 20, 2026
97d6808
Stop tracking a Python bytecode file and ignore __pycache__ (follow-u…
erikdarlingdata Oct 20, 2026
b09cc04
Query Store reads no longer say it may be OFF before the first interv…
erikdarlingdata Oct 20, 2026
c5b8290
Explain a SqlClient native library load failure instead of the TdsPar…
erikdarlingdata Oct 20, 2026
25c8041
PostgreSQL monitoring connections pin search_path at sign-in; extensi…
erikdarlingdata Oct 20, 2026
05f6d34
Azure SQL Database procedures keep their plans again (#6182) (#6193)
erikdarlingdata Oct 20, 2026
86bd19c
Azure SQL Database: say what the engine lacks in find_database, web p…
erikdarlingdata Oct 20, 2026
1034f05
Make the screen reader row-rename scroll reach every row on any runne…
erikdarlingdata Oct 20, 2026
9aef204
FinOps verdict needs enough samples and reads high stolen memory as p…
erikdarlingdata Oct 20, 2026
669b437
Azure SQL Database: tempdb reads and Collection Health say a containe…
erikdarlingdata Oct 20, 2026
bde13ad
Bring get_pg_top_queries, get_query_store_top and get_client_timeouts…
erikdarlingdata Oct 20, 2026
821e98d
Web dashboard polish: badge name, select style, tab scroll, repeated …
erikdarlingdata Oct 20, 2026
aba5186
Viewer and Lite: Invoke opens a server from the sidebar or a card; al…
erikdarlingdata Oct 20, 2026
03dfb3a
An offline PostgreSQL target no longer reads as healthy on panels, th…
erikdarlingdata Oct 20, 2026
f635e33
Query Store cut-empty live tests hold the statement until the budget …
erikdarlingdata Oct 20, 2026
c1bf1c7
Sensitive-statement tests assert on work and judge without a load-sen…
erikdarlingdata Oct 20, 2026
0091762
The Query Store history time test no longer fails when another Viewer…
erikdarlingdata Oct 20, 2026
d2da545
kill_session ends one session on an Azure SQL Database, from inside t…
erikdarlingdata Oct 20, 2026
184e019
Keep five MCP reads within the response budget and say when they are …
erikdarlingdata Oct 20, 2026
871beae
A collector the server keeps refusing is tried less often (#6186) (#6…
erikdarlingdata Oct 20, 2026
ca152e1
The PostgreSQL index check runs under its own login with only the gra…
erikdarlingdata Oct 20, 2026
683cdba
Plan changes answer with an undo, and the agent no longer waits for a…
erikdarlingdata Oct 20, 2026
b4ff375
MCP reads name Azure SQL Database servers by display name (#6179) (#6…
erikdarlingdata Oct 20, 2026
b30bfa7
A row with an empty cell no longer costs every server its delta basel…
erikdarlingdata Oct 20, 2026
214d70c
PostgreSQL collectors send a query that fits the target's version (#6…
erikdarlingdata Oct 20, 2026
384220e
The deadlock graph's detail pane shows each process's sql_handle (#62…
erikdarlingdata Oct 20, 2026
e9f9ecd
PostgreSQL target connections load no types, so the first query is ou…
erikdarlingdata Oct 20, 2026
e5478d7
A WPF test body that does not return now says where it stopped, and t…
erikdarlingdata Oct 20, 2026
1539d96
Scheduled reports log as reports, repeated delivery failures log once…
erikdarlingdata Oct 20, 2026
17987c5
Diagnostics bundle finds the managed store the service runs, and says…
erikdarlingdata Oct 20, 2026
ce62c83
The web deadlock graph's process pane wraps its SQL handle note and e…
erikdarlingdata Oct 20, 2026
f4e6d2c
Top-N reads answer empty, not unavailable, when the collector ran and…
erikdarlingdata Oct 20, 2026
0da28da
PostgreSQL Query Shapes statement grid no longer looks blank, and the…
erikdarlingdata Oct 20, 2026
9a163c6
Starter agent file: say which model to use for which step (#5632) (#6…
erikdarlingdata Oct 20, 2026
0993a14
Custom views and notebooks get shared folders, personal views and an …
erikdarlingdata Oct 20, 2026
e39c5b6
The forced-plans collector reads no plan XML; variants and plan guide…
erikdarlingdata Oct 20, 2026
cb64334
Custom alert rules get an optional expiry (#6235) (#6242)
erikdarlingdata Oct 20, 2026
3ec8001
Custom Views: create and delete refresh the sidebar at once, and the …
erikdarlingdata Oct 20, 2026
d614126
Fleet Overview heading no longer sits under its controls; Help > Abou…
erikdarlingdata Oct 20, 2026
375ffd9
A queued command that nobody claims within 15 minutes is failed, not …
erikdarlingdata Oct 20, 2026
535a861
The hourly store-size row is a catalog estimate; one sweep a day stay…
erikdarlingdata Oct 20, 2026
1dd0517
Export the investigation cases that have feedback, to test your own a…
erikdarlingdata Oct 20, 2026
904d82f
The forced-plans collector reads the forced plans and their object id…
erikdarlingdata Oct 20, 2026
df3ffba
Darling MCP can stop, start and reschedule a collector, and read its …
erikdarlingdata Oct 20, 2026
9c2faf6
A recorded change opens a watch window, and an investigation opens on…
erikdarlingdata Oct 20, 2026
d31c5bf
Darling MCP: incident controls (collection state, connection test, pu…
erikdarlingdata Oct 21, 2026
8aa36ea
Plan correction and Query Store health skip a readable secondary, as …
erikdarlingdata Oct 21, 2026
5f91443
Darling MCP: notification routes, alert history dismissal and a quiet…
erikdarlingdata Oct 21, 2026
d064c5b
Diagnostics bundle: a known name inside an ordinary word is left read…
erikdarlingdata Oct 21, 2026
4bd88d4
A fleet Query Store panel reads the interval table for the servers th…
erikdarlingdata Oct 21, 2026
f9075e3
Agent dispatch docs, starter instructions and log lines match what sh…
erikdarlingdata Oct 21, 2026
80bd5aa
list_servers answers an empty registry with the empty status envelope…
erikdarlingdata Oct 21, 2026
72f72fe
A darling.json change to a server's remediation login now reaches an …
erikdarlingdata Oct 21, 2026
e67e532
FinOps page test counts the one empty branch with Assert.Single (#627…
erikdarlingdata Oct 21, 2026
65525da
A plan undo the server accepted is always journaled, and the write re…
erikdarlingdata Oct 21, 2026
f76dd29
The web can close an investigation request, write an agent note and s…
erikdarlingdata Oct 21, 2026
e51803d
The Query Store regressions read takes its baseline's closed days fro…
erikdarlingdata Oct 21, 2026
5d284b1
The Query Store collector fetches text and plans only for each databa…
erikdarlingdata Oct 21, 2026
739ac91
The web can create, edit and delete agent routes and edit environment…
erikdarlingdata Oct 21, 2026
f86633f
MCP tag tools can no longer change which servers an agent route cover…
erikdarlingdata Oct 21, 2026
5d03bcd
The Overview Blocking & Deadlocking lane draws one event as a visible…
erikdarlingdata Oct 21, 2026
25234f0
Lite's query window floor reads take the cancellation token last (#6290)
erikdarlingdata Oct 21, 2026
94e7518
A person or an MCP client can turn a server's agent action switches o…
erikdarlingdata Oct 21, 2026
6b0baae
Sign in again after a web session expiry reaches the sign-in (#6278) …
erikdarlingdata Oct 21, 2026
29d2821
A link opened from another site now opens signed in (#6286) (#6289)
erikdarlingdata Oct 21, 2026
cc0b5cb
Read the plan-change markers with GROUP BY, not DISTINCT, so Timescal…
erikdarlingdata Oct 21, 2026
1deff54
Lite's Query Store regressions read shows Regular, Aborted and Except…
erikdarlingdata Oct 21, 2026
be291ff
Default trace collector: drop the server-side Sort that timed out wai…
erikdarlingdata Oct 21, 2026
4b8bca9
Coordinator orders: measure before planning, stack same-file PRs, bro…
erikdarlingdata Oct 21, 2026
e918578
Azure XE fan-out: a connection failure wins over a missing-session no…
erikdarlingdata Oct 21, 2026
9ddb0c7
MCP answers no longer misstate sub-millisecond CPU, the monitor's own…
erikdarlingdata Oct 21, 2026
9879fe4
Agent changes names the server, Admin tabs wrap, server header stays …
erikdarlingdata Oct 21, 2026
7629b06
The web charts file I/O throughput, tempdb size and file I/O, and col…
erikdarlingdata Oct 21, 2026
87d94c0
Web dashboard: the sidebar collapses to an overlay menu at phone widt…
erikdarlingdata Oct 21, 2026
193b511
The plan regression finding now also catches queries whose runs time …
erikdarlingdata Oct 21, 2026
9256731
Web: a Compare picker turns Top Queries, Top Procedures and Query Sto…
erikdarlingdata Oct 21, 2026
f0f3c07
The web shows the lead-blocker tree of a blocked process report (#629…
erikdarlingdata Oct 21, 2026
a8a5800
Web: chart the System Events corruption and contention counters (#629…
erikdarlingdata Oct 21, 2026
611912c
Darling live tests: scratch databases run with TimescaleDB on by defa…
erikdarlingdata Oct 21, 2026
ef4fb27
WPF tests: the wait for application idle is bounded and names what it…
erikdarlingdata Oct 21, 2026
3525bf4
Client Timeouts bars are relative to each server's own normal rate (#…
erikdarlingdata Oct 21, 2026
81b2635
Query Store collector: stage each view before joining in the payload …
erikdarlingdata Oct 21, 2026
a044c18
The web Active Queries tab picks a stored snapshot (#6302) (#6332)
erikdarlingdata Oct 21, 2026
b243130
Alerts show how many days in a row they fired at the same time (#6261…
erikdarlingdata Oct 21, 2026
639ab92
The web shows the Daily Summary as a month calendar (#6294) (#6327)
erikdarlingdata Oct 21, 2026
323658b
Pre-push check: a FULL test-map answer stops instead of reporting gre…
erikdarlingdata Oct 21, 2026
d026c41
Darling tests: no nullable warning in the client-timeout baseline tes…
erikdarlingdata Oct 21, 2026
0d4f345
The web draws the Query Heatmap as a colour grid (#6293) (#6329)
erikdarlingdata Oct 21, 2026
ac1c979
The web charts memory grant sizing and activity over the window (#629…
erikdarlingdata Oct 21, 2026
2621790
The web opens a per-row history for Top Queries and Top Procedures (#…
erikdarlingdata Oct 21, 2026
101c525
The web Overview draws five strips on one axis with a shared crosshai…
erikdarlingdata Oct 21, 2026
0acc499
CHANGELOG: splice the 3.11.0 entries merged so far (#6355)
erikdarlingdata Oct 21, 2026
04046d1
MCP add_servers and edit_server recognise one machine under another n…
erikdarlingdata Oct 21, 2026
d015668
Top Queries by CPU: rank only the ranked groups' procedure handles, n…
erikdarlingdata Oct 21, 2026
7d10470
Test the runner's collection note through RunAsync, merge the payload…
erikdarlingdata Oct 21, 2026
144979b
Web: compare deltas coloured, compare notice only when it applies, fu…
erikdarlingdata Oct 21, 2026
ff453fb
Overview strips: draw at the real width on first load, label time in …
erikdarlingdata Oct 21, 2026
d583461
A server's host can no longer be a named pipe or a UNC path (#6352) (…
erikdarlingdata Oct 21, 2026
7c2deaa
Web History panels: stack with Plan at a fixed height, show the bucke…
erikdarlingdata Oct 21, 2026
2f9159b
Stop following a mirroring partner a monitored server names (#6365) (…
erikdarlingdata Oct 21, 2026
7e8bac6
Flag registrations that report the same machine (#6351) (#6368)
erikdarlingdata Oct 22, 2026
96eaa38
Cache the host-tail characters as SearchValues (CA1870, #6352) (#6369)
erikdarlingdata Oct 22, 2026
591be36
The web narrows its grids with a time-range slicer (#6301) (#6354)
erikdarlingdata Oct 22, 2026
7f050b4
Seed the collection-health plan test from a fixed chunk layout, not t…
erikdarlingdata Oct 22, 2026
4eaaa84
Query Store reads can come from a readable replica (#6254) (#6342)
erikdarlingdata Oct 22, 2026
4a061e3
Clock-dependent Darling tests: the regressions interval-table test no…
erikdarlingdata Oct 22, 2026
bf850bf
Make the wide-table gate's legacy-row probe opt-in (#6267 follow-up) …
erikdarlingdata Oct 22, 2026
aa6dd9c
Known-pattern entries show with matching alerts (#6334) (#6367)
erikdarlingdata Oct 22, 2026
b0bd088
Put Git's sh folder on PATH in the compose setup script tests (#6379)…
erikdarlingdata Oct 22, 2026
3dda027
Change watch: detect at most once an hour per target (#6234) (#6372)
erikdarlingdata Oct 22, 2026
6835ac9
The PLAN_REGRESSION drill-down reads closed days from the per-day tot…
erikdarlingdata Oct 22, 2026
d9f7528
The Viewer's Collection Health tab can run all collectors now or one …
erikdarlingdata Oct 22, 2026
5c71aab
Darling MCP serves unmute_analysis_finding (#6383) (#6405)
erikdarlingdata Oct 22, 2026
ef94269
default_trace_events retries the Linux end-of-file error on the serve…
erikdarlingdata Oct 22, 2026
296d79a
File I/O no longer invents reads and writes on Hyperscale (#6438) (#6…
erikdarlingdata Oct 22, 2026
adbc39d
Reload route cache on every route field delivery reads (#6444) (#6447)
erikdarlingdata Oct 22, 2026
b89bee8
Web dashboard polish: Azure tempdb latency wording, System Events int…
erikdarlingdata Oct 22, 2026
091fd4a
Server-write slot: a second release waits until the first has freed t…
erikdarlingdata Oct 22, 2026
89680d8
Viewer: closed combo boxes show the display name, and editable combos…
erikdarlingdata Oct 22, 2026
9dcf037
get_wait_trend: empty, not not_collected, for a stored wait type with…
erikdarlingdata Oct 22, 2026
af5cf20
Docs: name the Lite installer PerformanceMonitorLite-lite-Setup.exe (…
erikdarlingdata Oct 22, 2026
87136d1
Quiet-window create reads the caller's clock, not the real one (#6465…
erikdarlingdata Oct 22, 2026
9dc83f1
Viewer Job History: pick an Azure SQL Database, say it has no SQL Age…
erikdarlingdata Oct 22, 2026
57941e4
Retention: skip the promoted legacy row delete, prune query_store_tex…
erikdarlingdata Oct 22, 2026
a52632a
On a managed store the web's refused-write messages no longer name an…
erikdarlingdata Oct 22, 2026
325f128
Test map: WaitForIdle takes its priority through an overload, so AltC…
erikdarlingdata Oct 22, 2026
ae06477
Warn when an Extended Events ring buffer is truncated (#6441, part 1)…
erikdarlingdata Oct 22, 2026
8fea705
Release 3.11.0: version, changelog, archive (#6306)
erikdarlingdata Oct 22, 2026
56a948d
Read the regressions label text as a keyed lookup, not a join (part o…
erikdarlingdata Oct 22, 2026
93a67de
Release 3.10.0 (#5645) (#6483)
erikdarlingdata Oct 22, 2026
7d6912c
Merge main into dev so the 3.11.0 release PR has no conflicts (dev's …
erikdarlingdata Oct 22, 2026
71bfee7
Merge pull request #6485 from erikdarlingdata/fix/3110-sync-main-into…
erikdarlingdata Oct 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
22 changes: 10 additions & 12 deletions .claude/skills/duckdb/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,22 @@ disable-model-invocation: false

# Query Lite's DuckDB Database

Safely query the PerformanceMonitor Lite DuckDB database using Python. This skill exists because DuckDB only allows one read-write connection at a time. When Lite is running (collecting data), it holds the write connection. External readers MUST connect in read-only mode.
Safely query the PerformanceMonitor Lite DuckDB database using Python. This skill exists because DuckDB only allows one read-write connection at a time. When Lite is running (collecting data), it holds the write connection, so an external reader connects in read-only mode.

## Database Location

Since the #1832 fix (data moved OUT of Velopack's install root, which Setup.exe deletes), the store lives at:
The store lives at:
```
%LOCALAPPDATA%\PerformanceMonitorLite-Data\monitor.duckdb
```
Builds from BEFORE that fix (3.3.0 and earlier) keep it at the old path, and the file only moves when a fixed build first runs:
Lite 3.3.0 and earlier kept it at the path below, and the file moves to the path above only when a newer build first runs. Check the path above first, and fall back to this one when it does not exist:
```
%LOCALAPPDATA%\PerformanceMonitorLite\monitor.duckdb
```
**Check the -Data path first; fall back to the old path if it does not exist.**

## How to Query (ALWAYS use this pattern)
## How to Query

Use Python with the `duckdb` module. **ALWAYS connect in read-only mode.**
Use Python with the `duckdb` module, always with this pattern:

```python
python -c "
Expand All @@ -40,13 +39,12 @@ con.close()
"
```

## CRITICAL RULES
## Rules

1. **ALWAYS use `read_only=True`** -- without this, the connection will be blocked by Lite's write lock and hang or fail
2. **NEVER use `duckdb.connect()` without `read_only=True`** -- the default is read-write which WILL conflict with the running app
3. **Use forward slashes** in the path (Python on Windows handles this fine)
4. **Close the connection** when done -- don't leave read locks dangling
5. **Use Python** -- duckdb 1.4.4 is installed
1. **Pass `read_only=True` to `duckdb.connect()`.** The default is read-write. Lite's write lock blocks a read-write connection, and it hangs or fails.
2. **Use forward slashes** in the path (Python on Windows handles this fine)
3. **Close the connection** when done, so no read lock is left dangling
4. **Use Python** with its `duckdb` module

## Common Queries

Expand Down
158 changes: 101 additions & 57 deletions .claude/skills/lane-orders/SKILL.md

Large diffs are not rendered by default.

246 changes: 246 additions & 0 deletions .claude/skills/lane-orders/rig-common.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
# Shared by rig-start.ps1, rig-stop.ps1 and rig-sweep.ps1 (#5787). Dot-source only.
#
# A rig is one throwaway PostgreSQL cluster an agent starts for live test classes:
# <RigDir>\pgsql\ the runtime zip, extracted
# <RigDir>\data\ the cluster (initdb)
# <RigDir>\pg.log the server log
# Every rig is registered in one file outside every repo, so a sweep can find the ones whose agent is gone.

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# One documented location, outside every repo and never committed. Tab-separated, no header, one line per cluster:
# data_dir <TAB> pid <TAB> port <TAB> started_utc <TAB> owner
$script:RigRegistryPath = 'C:\GitHub\worktrees\.rig-registry.tsv'

# The only places a rig data directory may live. Everything else is refused, including a registry line that points
# elsewhere: the main checkout, Program Files, ProgramData (the Darling service's own managed cluster) and the rest.
$script:RigAllowedRoots = @(
'C:\GitHub\worktrees\',
'C:\GitHub\PerformanceMonitor\.claude\worktrees\',
'C:\Users\edarl\AppData\Local\Temp\claude\'
)

# Top-level entries a rig folder holds. A folder with anything else in it keeps those files: only data and pgsql go.
$script:RigFolderEntries = @('pgsql', 'data', 'pg.log')

function ConvertTo-RigPath {
param([Parameter(Mandatory)][string]$Path)
$full = [System.IO.Path]::GetFullPath($Path.Replace('/', '\'))
return $full.TrimEnd('\')
}

# Returns $null when the path is an acceptable rig data directory, or the reason it is refused.
function Get-RigPathRefusal {
param([Parameter(Mandatory)][string]$DataDir)
$full = ConvertTo-RigPath $DataDir
$rigFolder = Split-Path -Parent $full
$root = $script:RigAllowedRoots | Where-Object { ($rigFolder + '\').StartsWith($_, [System.StringComparison]::OrdinalIgnoreCase) -and ($rigFolder + '\').Length -gt $_.Length } | Select-Object -First 1
if (-not $root) {
return "'$full' is not inside a rig folder under an allowed root ($($script:RigAllowedRoots -join ', '))."
}
if (Test-Path -LiteralPath (Join-Path $rigFolder '.git')) {
return "'$rigFolder' holds a .git entry: it is a repo or worktree root, not a rig folder."
}
foreach ($p in @($rigFolder, $full)) {
$item = Get-Item -LiteralPath $p -Force -ErrorAction SilentlyContinue
if ($item -and ($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
return "'$p' is a junction or symbolic link."
}
}
return $null
}

# Reads under the registry lock too: Remove-RigRegistryLine rewrites the whole file, and a reader outside the lock
# can meet a sharing violation or a half-written file (#5787 review).
function Read-RigRegistry {
$lines = @(Invoke-RigRegistryLocked {
if (Test-Path -LiteralPath $script:RigRegistryPath) { Get-Content -LiteralPath $script:RigRegistryPath }
})
$rows = foreach ($line in $lines) {
if ([string]::IsNullOrWhiteSpace($line)) { continue }
$f = $line.Split("`t")
[pscustomobject]@{
DataDir = $f[0]
Pid = if ($f.Count -gt 1) { $f[1] } else { '' }
Port = if ($f.Count -gt 2) { $f[2] } else { '' }
StartedUtc = if ($f.Count -gt 3) { $f[3] } else { '' }
Owner = if ($f.Count -gt 4) { $f[4] } else { '' }
Line = $line
}
}
return @($rows)
}

# Every registry read and change runs under one machine-wide mutex: lanes start and stop rigs at the same time.
# A named mutex is owned by its thread, so a holder may nest calls (rig-sweep.ps1 holds it across one rig's
# decide-and-stop, and Stop-RigCluster takes it again to drop the line). The wait covers one rig's stop (up to
# about 90 seconds) by another caller.
function Invoke-RigRegistryLocked {
param([Parameter(Mandatory)][scriptblock]$Body)
$mutex = [System.Threading.Mutex]::new($false, 'Global\PerformanceMonitorRigRegistry')
$held = $false
try {
try { $held = $mutex.WaitOne([TimeSpan]::FromSeconds(180)) } catch [System.Threading.AbandonedMutexException] { $held = $true }
if (-not $held) { throw "Timed out waiting for the rig registry lock." }
& $Body
}
finally {
if ($held) { $mutex.ReleaseMutex() }
$mutex.Dispose()
}
}

function Add-RigRegistryLine {
param([string]$DataDir, [int]$ProcessId, [int]$Port, [string]$Owner)
$started = [DateTime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ssZ')
$line = ($DataDir, $ProcessId, $Port, $started, ($Owner -replace "[`t`r`n]", ' ')) -join "`t"
Invoke-RigRegistryLocked {
$dir = Split-Path -Parent $script:RigRegistryPath
if (-not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Path $dir | Out-Null }
Add-Content -LiteralPath $script:RigRegistryPath -Value $line -Encoding utf8NoBOM
}
}

function Remove-RigRegistryLine {
param([Parameter(Mandatory)][string]$DataDir)
$target = ConvertTo-RigPath $DataDir
Invoke-RigRegistryLocked {
if (-not (Test-Path -LiteralPath $script:RigRegistryPath)) { return }
$keep = @(Get-Content -LiteralPath $script:RigRegistryPath | Where-Object {
-not [string]::IsNullOrWhiteSpace($_) -and
-not [string]::Equals((ConvertTo-RigPath ($_.Split("`t")[0])), $target, [System.StringComparison]::OrdinalIgnoreCase)
})
Set-Content -LiteralPath $script:RigRegistryPath -Value $keep -Encoding utf8NoBOM
}
}

# Every running postgres.exe postmaster (no --forkchild), with the data directory its command line names.
function Get-PostmasterProcesses {
$all = @(Get-CimInstance Win32_Process -Filter "Name = 'postgres.exe'")
foreach ($p in $all) {
$cmd = [string]$p.CommandLine
if ($cmd -match '--forkchild') { continue }
$data = $null
if ($cmd -match '(?:^|\s)-D\s*"([^"]+)"') { $data = $Matches[1] }
elseif ($cmd -match '(?:^|\s)-D\s*(\S+)') { $data = $Matches[1] }
[pscustomobject]@{
ProcessId = [int]$p.ProcessId
DataDir = if ($data) { ConvertTo-RigPath $data } else { '' }
CommandLine = $cmd
Children = @($all | Where-Object { $_.ParentProcessId -eq $p.ProcessId }).Count
}
}
}

# True only when the PID is a live postgres.exe postmaster whose command line names this data directory.
function Test-RigPostmaster {
param([int]$ProcessId, [string]$DataDir)
$target = ConvertTo-RigPath $DataDir
$proc = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
if (-not $proc -or $proc.ProcessName -ne 'postgres') { return $false }
$cim = Get-CimInstance Win32_Process -Filter "ProcessId = $ProcessId"
if (-not $cim) { return $false }
$cmd = ([string]$cim.CommandLine).Replace('/', '\')
return $cmd.IndexOf($target, [System.StringComparison]::OrdinalIgnoreCase) -ge 0
}

# The postmaster PID from <data>\postmaster.pid, or 0.
function Get-RigPidFilePid {
param([string]$DataDir)
$pidFile = Join-Path $DataDir 'postmaster.pid'
if (-not (Test-Path -LiteralPath $pidFile)) { return 0 }
$first = Get-Content -LiteralPath $pidFile -TotalCount 1
$n = 0
if ([int]::TryParse(([string]$first).Trim(), [ref]$n)) { return $n }
return 0
}

# Stops the cluster in DataDir (pg_ctl first, then the verified postmaster PID), deletes the rig, drops its
# registry line. Returns an object saying what it did. Refuses any path Get-RigPathRefusal rejects, touching nothing.
function Stop-RigCluster {
param([Parameter(Mandatory)][string]$DataDir, [int]$RegisteredPid = 0)
$data = ConvertTo-RigPath $DataDir
$refusal = Get-RigPathRefusal $data
if ($refusal) {
return [pscustomobject]@{ DataDir = $data; Refused = $refusal; Stopped = $false; Deleted = $false; Note = '' }
}
$rigFolder = Split-Path -Parent $data
$result = [pscustomobject]@{ DataDir = $data; Refused = $null; Stopped = $false; Deleted = $false; Note = '' }

if ((Test-Path -LiteralPath $data) -and -not (Test-Path -LiteralPath (Join-Path $data 'PG_VERSION'))) {
$result.Refused = "'$data' exists but holds no PG_VERSION: it is not a PostgreSQL data directory."
return $result
}

# The postmaster to stop: postmaster.pid first, the registered PID second; either must prove itself.
$postmaster = 0
foreach ($candidate in @((Get-RigPidFilePid $data), $RegisteredPid)) {
if ($candidate -gt 0 -and (Test-RigPostmaster -ProcessId $candidate -DataDir $data)) { $postmaster = $candidate; break }
}

if ($postmaster -eq 0) {
# A live postgres at the postmaster.pid PID that does not name this data directory: deleting under a running
# server would tear files out from under it, so keep everything and say so.
$filePid = Get-RigPidFilePid $data
$proc = if ($filePid -gt 0) { Get-Process -Id $filePid -ErrorAction SilentlyContinue } else { $null }
if ($proc -and $proc.ProcessName -eq 'postgres') {
$result.Refused = "postmaster.pid names live postgres PID $filePid, whose command line does not name '$data'; nothing touched."
return $result
}
}

if ($postmaster -gt 0) {
$children = @(Get-CimInstance Win32_Process -Filter "ParentProcessId = $postmaster AND Name = 'postgres.exe'" | ForEach-Object { [int]$_.ProcessId })
$pgCtl = Join-Path $rigFolder 'pgsql\bin\pg_ctl.exe'
if (Test-Path -LiteralPath $pgCtl) {
$out = & $pgCtl stop -D $data -m fast -w -t 60 2>&1
if ($LASTEXITCODE -ne 0) { $result.Note += "pg_ctl stop exited $LASTEXITCODE ($(($out | Out-String).Trim())); " }
}
else {
$result.Note += 'pg_ctl.exe missing; stopped the verified postmaster PID; '
}
if (Get-Process -Id $postmaster -ErrorAction SilentlyContinue) {
# Re-verify right before the kill: never stop a PID that is not this cluster's postmaster.
if (Test-RigPostmaster -ProcessId $postmaster -DataDir $data) {
Stop-Process -Id $postmaster -Force
}
}
# Backends exit on their own once the postmaster is gone; give them time, then stop the stragglers by PID.
$deadline = [DateTime]::UtcNow.AddSeconds(20)
while ([DateTime]::UtcNow -lt $deadline) {
$alive = @($children | Where-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue })
if ($alive.Count -eq 0 -and -not (Get-Process -Id $postmaster -ErrorAction SilentlyContinue)) { break }
Start-Sleep -Milliseconds 500
}
foreach ($c in $children) {
$proc = Get-Process -Id $c -ErrorAction SilentlyContinue
$cim = Get-CimInstance Win32_Process -Filter "ProcessId = $c"
if ($proc -and $proc.ProcessName -eq 'postgres' -and $cim -and $cim.ParentProcessId -eq $postmaster) {
Stop-Process -Id $c -Force
}
}
if (Get-Process -Id $postmaster -ErrorAction SilentlyContinue) {
$result.Note += "postmaster $postmaster still running; folder kept; "
return $result
}
$result.Stopped = $true
}

# Delete: the whole rig folder when it holds only rig entries; otherwise data and pgsql only.
if (Test-Path -LiteralPath $rigFolder) {
$extra = @(Get-ChildItem -LiteralPath $rigFolder -Force | Where-Object { $script:RigFolderEntries -notcontains $_.Name })
$targets = if ($extra.Count -eq 0) { @($rigFolder) } else { @($data, (Join-Path $rigFolder 'pgsql')) | Where-Object { Test-Path -LiteralPath $_ } }
if ($extra.Count -gt 0) { $result.Note += "rig folder holds other files ($(($extra.Name | Select-Object -First 3) -join ', ')); removed data and pgsql only; " }
foreach ($t in $targets) {
for ($attempt = 1; $attempt -le 5; $attempt++) {
try { Remove-Item -LiteralPath $t -Recurse -Force; break }
catch { if ($attempt -eq 5) { $result.Note += "delete of '$t' failed: $($_.Exception.Message); "; return $result } Start-Sleep -Seconds 2 }
}
}
$result.Deleted = $true
}

Remove-RigRegistryLine $data
return $result
}
88 changes: 88 additions & 0 deletions .claude/skills/lane-orders/rig-start.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<#
.SYNOPSIS
Starts a throwaway PostgreSQL cluster (a rig) for live test classes and registers it (#5787).
.DESCRIPTION
Unpacks the runtime zip into RigDir, runs initdb on RigDir\data with the settings CI's darling-pg job uses plus
UTC time zones, starts the server on Port, and appends one line to C:\GitHub\worktrees\.rig-registry.tsv so
rig-sweep.ps1 can stop it once its owner is gone. Stop it with rig-stop.ps1 -DataDir <RigDir>\data.
.EXAMPLE
pwsh -NoProfile -File C:\GitHub\PerformanceMonitor\.claude\skills\lane-orders\rig-start.ps1 -Port 55611 `
-RigDir C:\Users\edarl\AppData\Local\Temp\claude\<session>\scratchpad\fix-123-thing\rig -Owner C:\GitHub\PerformanceMonitor\.claude\worktrees\agent-x
#>
param(
[Parameter(Mandatory)][int]$Port,
# The rig folder: must not exist yet, or be empty, and must sit under an allowed root (see rig-common.ps1).
[Parameter(Mandatory)][string]$RigDir,
# The worktree path that uses the rig, or an agent id when there is none. The sweep stops the rig once a
# worktree-path owner no longer exists.
[Parameter(Mandatory)][string]$Owner,
[string]$Zip = 'C:\GitHub\PerformanceMonitor\Darling\artifacts\pg-runtime.zip'
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'rig-common.ps1')

$rig = ConvertTo-RigPath $RigDir
$data = Join-Path $rig 'data'
$refusal = Get-RigPathRefusal $data
if ($refusal) { throw "Refused: $refusal" }
if ((Test-Path -LiteralPath $rig) -and @(Get-ChildItem -LiteralPath $rig -Force).Count -gt 0) {
throw "Refused: '$rig' already exists and is not empty. Pick a new rig folder, or stop the old rig with rig-stop.ps1."
}
if (-not (Test-Path -LiteralPath $Zip)) { throw "Runtime zip '$Zip' not found. Run Darling\tools\fetch-pg-runtime.ps1." }
if (Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue) { throw "Port $Port is already in use." }
$taken = @(Read-RigRegistry | Where-Object { $_.Port -eq [string]$Port })
if ($taken.Count -gt 0) { throw "Port $Port is registered to '$($taken[0].DataDir)' (owner $($taken[0].Owner)). Pick another port, or run rig-sweep.ps1." }

$started = $false
try {
New-Item -ItemType Directory -Force -Path $rig | Out-Null
Expand-Archive -LiteralPath $Zip -DestinationPath $rig
$bin = Join-Path $rig 'pgsql\bin'
if (-not (Test-Path -LiteralPath (Join-Path $bin 'initdb.exe'))) { throw "The zip has no pgsql\bin\initdb.exe." }

$initOut = & (Join-Path $bin 'initdb.exe') -D $data -U darling -A trust --encoding=UTF8 2>&1
if ($LASTEXITCODE -ne 0) { throw "initdb failed ($LASTEXITCODE): $(($initOut | Select-Object -Last 5) -join ' ')" }

# The darling-pg job's settings in .github/workflows/build.yml, plus UTC: CI's runners run in UTC.
Add-Content -LiteralPath (Join-Path $data 'postgresql.conf') -Encoding utf8NoBOM -Value @(
"shared_preload_libraries = 'timescaledb,pg_stat_statements'",
"port = $Port",
"listen_addresses = '127.0.0.1'",
"timescaledb.max_background_workers = 74",
"max_worker_processes = 85",
"log_line_prefix = '%m [%p] %c '",
"timezone = 'UTC'",
"log_timezone = 'UTC'"
)

# The postmaster inherits every inheritable handle of the process that starts it, including the caller's
# stdout pipe, so a plain `pg_ctl -w start` (or Start-Process with redirection, which inherits too) holds a
# tool call open until the server stops. Start-Process without -NoNewWindow or redirection goes through
# ShellExecute in a hidden window, which passes no handles; pg.log carries everything pg_ctl would print.
$log = Join-Path $rig 'pg.log'
$pgCtl = Start-Process -FilePath (Join-Path $bin 'pg_ctl.exe') -PassThru -WindowStyle Hidden `
-ArgumentList @('start', '-D', "`"$data`"", '-o', "`"-p $Port`"", '-w', '-t', '90', '-l', "`"$log`"")
$null = $pgCtl.Handle
if (-not $pgCtl.WaitForExit(120000)) { throw "pg_ctl start did not return in 120 seconds." }
$started = $true
if ($pgCtl.ExitCode -ne 0) { throw "pg_ctl start failed ($($pgCtl.ExitCode)): $((Get-Content -LiteralPath $log -Tail 10 -ErrorAction SilentlyContinue) -join ' ')" }
if (-not (Select-String -LiteralPath $log -Pattern 'ready to accept connections' -Quiet)) { throw "The log at '$log' never said 'ready to accept connections'." }

$postmaster = Get-RigPidFilePid $data
if (-not (Test-RigPostmaster -ProcessId $postmaster -DataDir $data)) { throw "postmaster.pid PID $postmaster is not this cluster's postmaster." }

Add-RigRegistryLine -DataDir $data -ProcessId $postmaster -Port $Port -Owner $Owner
Write-Host "Rig started: data $data, PID $postmaster, port $Port, owner $Owner. Registered in $script:RigRegistryPath."
Write-Host "Stop it with: pwsh -NoProfile -File $(Join-Path $PSScriptRoot 'rig-stop.ps1') -DataDir $data"
}
catch {
# Leave nothing behind that no registry line points at.
$failure = $_
if ($started -or (Test-Path -LiteralPath $rig)) {
$cleanup = Stop-RigCluster -DataDir $data
if ($cleanup.Refused -or $cleanup.Note) { Write-Warning "Cleanup after the failed start: $($cleanup.Refused) $($cleanup.Note)" }
}
throw $failure
}
Loading
Loading