Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion Lite/Database/DuckDbInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void Dispose()
/// <summary>
/// Current schema version. Increment this when schema changes require table rebuilds.
/// </summary>
internal const int CurrentSchemaVersion = 16;
internal const int CurrentSchemaVersion = 17;

private readonly string _archivePath;

Expand Down Expand Up @@ -505,6 +505,23 @@ New tables only — no existing table changes needed. Tables created by
GetAllTableStatements() during initialization. */
_logger?.LogInformation("Running migration to v16: adding FinOps tables (database_size_stats, server_properties)");
}

if (fromVersion < 17)
{
/* v17: Added volume-level drive space columns to database_size_stats.
Columns appended at end — safe for DuckDB appender positional writes. */
_logger?.LogInformation("Running migration to v17: adding volume stats columns to database_size_stats");
try
{
await ExecuteNonQueryAsync(connection, "ALTER TABLE database_size_stats ADD COLUMN IF NOT EXISTS volume_mount_point VARCHAR");
await ExecuteNonQueryAsync(connection, "ALTER TABLE database_size_stats ADD COLUMN IF NOT EXISTS volume_total_mb DECIMAL(19,2)");
await ExecuteNonQueryAsync(connection, "ALTER TABLE database_size_stats ADD COLUMN IF NOT EXISTS volume_free_mb DECIMAL(19,2)");
}
catch
{
/* Table doesn't exist yet — will be created with correct schema below */
}
}
}

/// <summary>
Expand Down
5 changes: 4 additions & 1 deletion Lite/Database/Schema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,10 @@ auto_growth_mb DECIMAL(19,2),
max_size_mb DECIMAL(19,2),
recovery_model_desc VARCHAR,
compatibility_level INTEGER,
state_desc VARCHAR
state_desc VARCHAR,
volume_mount_point VARCHAR,
volume_total_mb DECIMAL(19,2),
volume_free_mb DECIMAL(19,2)
)";

public const string CreateDatabaseSizeStatsIndex = @"
Expand Down
32 changes: 26 additions & 6 deletions Lite/Services/RemoteCollectorService.DatabaseSize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public partial class RemoteCollectorService
{
/// <summary>
/// Collects per-file database sizes for growth trending and capacity planning.
/// On-prem: queries sys.master_files + sys.databases for all online databases.
/// Azure SQL DB: queries sys.database_files for the single database.
/// On-prem: queries sys.master_files + sys.databases + dm_os_volume_stats for file and drive context.
/// Azure SQL DB: queries sys.database_files for the single database (no volume stats available).
/// </summary>
private async Task<int> CollectDatabaseSizeStatsAsync(ServerConnection server, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -63,10 +63,17 @@ ELSE CONVERT(decimal(19,2), mf.max_size * 8.0 / 1024.0)
compatibility_level =
CONVERT(int, d.compatibility_level),
state_desc =
d.state_desc
d.state_desc,
volume_mount_point =
RTRIM(vs.volume_mount_point),
volume_total_mb =
CONVERT(decimal(19,2), vs.total_bytes / 1048576.0),
volume_free_mb =
CONVERT(decimal(19,2), vs.available_bytes / 1048576.0)
FROM sys.master_files AS mf
JOIN sys.databases AS d
ON d.database_id = mf.database_id
CROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.file_id) AS vs
WHERE d.state_desc = N'ONLINE'
ORDER BY
d.name,
Expand Down Expand Up @@ -106,7 +113,13 @@ ELSE CONVERT(decimal(19,2), df.max_size * 8.0 / 1024.0)
compatibility_level =
CONVERT(int, NULL),
state_desc =
N'ONLINE'
N'ONLINE',
volume_mount_point =
CONVERT(nvarchar(256), NULL),
volume_total_mb =
CONVERT(decimal(19,2), NULL),
volume_free_mb =
CONVERT(decimal(19,2), NULL)
FROM sys.database_files AS df
ORDER BY
df.file_id
Expand All @@ -123,7 +136,8 @@ ORDER BY
var rows = new List<(string DatabaseName, int DatabaseId, int FileId, string FileTypeDesc,
string FileName, string PhysicalName, decimal TotalSizeMb, decimal? UsedSizeMb,
decimal? AutoGrowthMb, decimal? MaxSizeMb, string? RecoveryModel,
int? CompatibilityLevel, string? StateDesc)>();
int? CompatibilityLevel, string? StateDesc, string? VolumeMountPoint,
decimal? VolumeTotalMb, decimal? VolumeFreeMb)>();

var sqlSw = Stopwatch.StartNew();
using var sqlConnection = await CreateConnectionAsync(server, cancellationToken);
Expand All @@ -146,7 +160,10 @@ ORDER BY
reader.IsDBNull(9) ? null : reader.GetDecimal(9),
reader.IsDBNull(10) ? null : reader.GetString(10),
reader.IsDBNull(11) ? null : reader.GetInt32(11),
reader.IsDBNull(12) ? null : reader.GetString(12)));
reader.IsDBNull(12) ? null : reader.GetString(12),
reader.IsDBNull(13) ? null : reader.GetString(13),
reader.IsDBNull(14) ? null : reader.GetDecimal(14),
reader.IsDBNull(15) ? null : reader.GetDecimal(15)));
}
sqlSw.Stop();

Expand Down Expand Up @@ -178,6 +195,9 @@ ORDER BY
.AppendValue(r.RecoveryModel)
.AppendValue(r.CompatibilityLevel)
.AppendValue(r.StateDesc)
.AppendValue(r.VolumeMountPoint)
.AppendValue(r.VolumeTotalMb)
.AppendValue(r.VolumeFreeMb)
.EndRow();
rowsCollected++;
}
Expand Down
3 changes: 3 additions & 0 deletions install/02_create_tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1430,6 +1430,9 @@ BEGIN
recovery_model_desc nvarchar(12) NULL,
compatibility_level integer NULL,
state_desc nvarchar(60) NULL,
volume_mount_point nvarchar(256) NULL,
volume_total_mb decimal(19,2) NULL,
volume_free_mb decimal(19,2) NULL,
/*Analysis helpers - computed columns*/
free_space_mb AS
(
Expand Down
3 changes: 3 additions & 0 deletions install/06_ensure_collection_table.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,9 @@ BEGIN
recovery_model_desc nvarchar(12) NULL,
compatibility_level integer NULL,
state_desc nvarchar(60) NULL,
volume_mount_point nvarchar(256) NULL,
volume_total_mb decimal(19,2) NULL,
volume_free_mb decimal(19,2) NULL,
free_space_mb AS
(
total_size_mb - used_size_mb
Expand Down
29 changes: 24 additions & 5 deletions install/52_collect_database_size_stats.sql
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ BEGIN
max_size_mb,
recovery_model_desc,
compatibility_level,
state_desc
state_desc,
volume_mount_point,
volume_total_mb,
volume_free_mb
)
SELECT
collection_time = @start_time,
Expand Down Expand Up @@ -147,7 +150,10 @@ BEGIN
recovery_model_desc =
CONVERT(nvarchar(12), DATABASEPROPERTYEX(DB_NAME(), N'Recovery')),
compatibility_level = NULL,
state_desc = N'ONLINE'
state_desc = N'ONLINE',
volume_mount_point = NULL,
volume_total_mb = NULL,
volume_free_mb = NULL
FROM sys.database_files AS df
OPTION(RECOMPILE);

Expand Down Expand Up @@ -200,7 +206,10 @@ BEGIN
max_size_mb,
recovery_model_desc,
compatibility_level,
state_desc
state_desc,
volume_mount_point,
volume_total_mb,
volume_free_mb
)
SELECT
collection_time = @start_time,
Expand Down Expand Up @@ -234,9 +243,16 @@ BEGIN
END,
recovery_model_desc = d.recovery_model_desc,
compatibility_level = d.compatibility_level,
state_desc = d.state_desc
state_desc = d.state_desc,
volume_mount_point =
RTRIM(vs.volume_mount_point),
volume_total_mb =
CONVERT(decimal(19,2), vs.total_bytes / 1048576.0),
volume_free_mb =
CONVERT(decimal(19,2), vs.available_bytes / 1048576.0)
FROM sys.database_files AS df
CROSS JOIN sys.databases AS d
CROSS APPLY sys.dm_os_volume_stats(DB_ID(), df.file_id) AS vs
WHERE d.database_id = DB_ID();';

EXECUTE sys.sp_executesql
Expand Down Expand Up @@ -277,7 +293,10 @@ BEGIN
dss.total_size_mb,
dss.used_size_mb,
dss.free_space_mb,
dss.used_pct
dss.used_pct,
dss.volume_mount_point,
dss.volume_total_mb,
dss.volume_free_mb
FROM collect.database_size_stats AS dss
WHERE dss.collection_time = @start_time
ORDER BY
Expand Down
41 changes: 41 additions & 0 deletions upgrades/2.1.0-to-2.2.0/06_add_volume_stats_columns.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright 2026 Darling Data, LLC
https://www.erikdarling.com/

Upgrade from 2.1.0 to 2.2.0
Adds volume-level drive space columns to database_size_stats.
*/

SET ANSI_NULLS ON;
SET ANSI_PADDING ON;
SET ANSI_WARNINGS ON;
SET ARITHABORT ON;
SET CONCAT_NULL_YIELDS_NULL ON;
SET QUOTED_IDENTIFIER ON;
SET NUMERIC_ROUNDABORT OFF;
SET IMPLICIT_TRANSACTIONS OFF;
SET STATISTICS TIME, IO OFF;
GO

USE PerformanceMonitor;
GO

IF NOT EXISTS
(
SELECT
1/0
FROM sys.columns
WHERE object_id = OBJECT_ID(N'collect.database_size_stats', N'U')
AND name = N'volume_mount_point'
)
BEGIN
ALTER TABLE
collect.database_size_stats
ADD
volume_mount_point nvarchar(256) NULL,
volume_total_mb decimal(19,2) NULL,
volume_free_mb decimal(19,2) NULL;

PRINT 'Added volume_mount_point, volume_total_mb, volume_free_mb to collect.database_size_stats';
END;
GO
1 change: 1 addition & 0 deletions upgrades/2.1.0-to-2.2.0/upgrade.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
03_compress_procedure_stats.sql
04_create_tracking_tables.sql
05_add_finops_collectors.sql
06_add_volume_stats_columns.sql
Loading