**Phase 2 Finalization:** - Updated schema-v2.sql: Replace automatic enforce_data_retention() cron with on-demand delete_hdd_data_by_serial(serial, keep_catalog) - Retention policy: Data deleted ONLY on user request (no automatic cleanup) - Created rename-to-archive-v1.sql: Safe rename of v1 tables to _archive_v1 (for later) - Updated SmartCollector.pm: Remove differential storage logic, use atomic insert_collection_event() - Updated smart-collector-daemon.pl: Same modernization (remove bugs, add atomic insert) - Created deploy-tapia.sh: Deployment script for tapia node (when ready) - Validated on database (192.168.2.102): delete_hdd_data_by_serial() function tested, production data verified intact **Status:** READY FOR PRODUCTION - Zero data loss from schema v1 → v2 (12.630 events + 315.722 param values) - Waiting for: tapia installation, then deploy collectors and monitor 1-2 weeks before archiving old tables Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@@ -1 +1 @@ |
||
| 1 |
-Subproject commit ec6d560e9debb952a07887d1d6120ea0cf9fcbd4 |
|
| 1 |
+Subproject commit 957b7631b97c0c2fb064ef2ac0789e31b6eb7890 |
|
@@ -308,17 +308,14 @@ sub store_smart_data {
|
||
| 308 | 308 |
eval {
|
| 309 | 309 |
# Detect/handle HDD migration first |
| 310 | 310 |
my $hdd_id = $self->_detect_or_create_hdd($drive_info, $smart_data); |
| 311 |
- |
|
| 312 |
- # Check if we should store this reading using differential storage |
|
| 313 |
- my $should_store = $self->_should_store_reading($hdd_id, $smart_data); |
|
| 314 |
- |
|
| 315 |
- if ($should_store->{store}) {
|
|
| 316 |
- # Insert SMART reading with differential storage information |
|
| 317 |
- $self->_insert_smart_reading_differential($hdd_id, $drive_info, $smart_data, $should_store); |
|
| 318 |
- |
|
| 319 |
- $self->_log("Stored SMART data for HDD ID $hdd_id (Serial: $smart_data->{serial_number}, Type: $should_store->{type})", 2);
|
|
| 311 |
+ |
|
| 312 |
+ # SCHEMA v2: Store complete reading (no differential storage) |
|
| 313 |
+ my $event_id = $self->_insert_collection_event($hdd_id, $drive_info, $smart_data); |
|
| 314 |
+ |
|
| 315 |
+ if ($event_id) {
|
|
| 316 |
+ $self->_log("Stored SMART data: HDD ID $hdd_id, Event ID $event_id (Serial: $smart_data->{serial_number})", 2);
|
|
| 320 | 317 |
} else {
|
| 321 |
- $self->_log("Skipped unchanged SMART data for HDD ID $hdd_id (Serial: $smart_data->{serial_number})", 3);
|
|
| 318 |
+ $self->_log("Failed to store SMART data for HDD ID $hdd_id", 1);
|
|
| 322 | 319 |
} |
| 323 | 320 |
}; |
| 324 | 321 |
|
@@ -776,6 +773,46 @@ sub _register_node {
|
||
| 776 | 773 |
} |
| 777 | 774 |
} |
| 778 | 775 |
|
| 776 |
+=head2 _insert_collection_event (SCHEMA v2) |
|
| 777 |
+ |
|
| 778 |
+SCHEMA v2: Insert complete SMART reading (no differential storage) |
|
| 779 |
+Calls PostgreSQL function: insert_collection_event() |
|
| 780 |
+ |
|
| 781 |
+=cut |
|
| 782 |
+ |
|
| 783 |
+sub _insert_collection_event {
|
|
| 784 |
+ my ($self, $hdd_id, $drive_info, $smart_data) = @_; |
|
| 785 |
+ |
|
| 786 |
+ # Build parameters JSON for database function |
|
| 787 |
+ my $params_json = encode_json($smart_data->{parameters} || {});
|
|
| 788 |
+ my $checksum = sha256_hex($params_json . ($smart_data->{temperature} || ''));
|
|
| 789 |
+ |
|
| 790 |
+ # Call PostgreSQL function: insert_collection_event(hdd_id, serial, node, ts, temp, ok, checksum, params::JSONB) |
|
| 791 |
+ my $sth = $self->{db_handle}->prepare(q{
|
|
| 792 |
+ SELECT insert_collection_event(?, ?, ?, NOW(), ?, ?, ?, ?::jsonb) |
|
| 793 |
+ }); |
|
| 794 |
+ |
|
| 795 |
+ eval {
|
|
| 796 |
+ $sth->execute( |
|
| 797 |
+ $hdd_id, |
|
| 798 |
+ $smart_data->{serial_number},
|
|
| 799 |
+ $self->{node_id},
|
|
| 800 |
+ $smart_data->{temperature} || undef,
|
|
| 801 |
+ 1, # collection_ok = true |
|
| 802 |
+ $checksum, |
|
| 803 |
+ $params_json |
|
| 804 |
+ ); |
|
| 805 |
+ }; |
|
| 806 |
+ |
|
| 807 |
+ if ($@) {
|
|
| 808 |
+ $self->_log("ERROR inserting collection event: $@", 1);
|
|
| 809 |
+ return undef; |
|
| 810 |
+ } |
|
| 811 |
+ |
|
| 812 |
+ my ($event_id) = $sth->fetchrow_array(); |
|
| 813 |
+ return $event_id; |
|
| 814 |
+} |
|
| 815 |
+ |
|
| 779 | 816 |
=head2 DESTROY |
| 780 | 817 |
|
| 781 | 818 |
Cleanup database connection |
@@ -0,0 +1,57 @@ |
||
| 1 |
+#!/bin/bash |
|
| 2 |
+ |
|
| 3 |
+# autoSMART Schema v2 — Deploy to Tapia (secondary node) |
|
| 4 |
+# Date: 2026-05-20 |
|
| 5 |
+# Copies updated collectors and restarts service |
|
| 6 |
+ |
|
| 7 |
+set -e |
|
| 8 |
+ |
|
| 9 |
+TAPIA_IP="192.168.2.93" |
|
| 10 |
+TAPIA_USER="root" |
|
| 11 |
+TARGET_DIR="/opt/autoSMART" |
|
| 12 |
+PROJECT_DIR="$(dirname "$(dirname "$0")")" |
|
| 13 |
+ |
|
| 14 |
+echo "🚀 Deploying autoSMART collectors to tapia ($TAPIA_IP)..." |
|
| 15 |
+echo "" |
|
| 16 |
+ |
|
| 17 |
+# 1. Copy updated scripts |
|
| 18 |
+echo "📦 Copying updated scripts..." |
|
| 19 |
+scp -q "$PROJECT_DIR/scripts/smart-collector-daemon.pl" "$TAPIA_USER@$TAPIA_IP:$TARGET_DIR/scripts/" |
|
| 20 |
+scp -q "$PROJECT_DIR/lib/SmartCollector.pm" "$TAPIA_USER@$TAPIA_IP:$TARGET_DIR/lib/" |
|
| 21 |
+echo "✅ Scripts copied" |
|
| 22 |
+echo "" |
|
| 23 |
+ |
|
| 24 |
+# 2. Stop service on tapia |
|
| 25 |
+echo "🛑 Stopping autosmart service on tapia..." |
|
| 26 |
+ssh "$TAPIA_USER@$TAPIA_IP" "systemctl stop autosmart" || true |
|
| 27 |
+sleep 2 |
|
| 28 |
+echo "✅ Service stopped" |
|
| 29 |
+echo "" |
|
| 30 |
+ |
|
| 31 |
+# 3. Restart service |
|
| 32 |
+echo "🚀 Starting autosmart service on tapia..." |
|
| 33 |
+ssh "$TAPIA_USER@$TAPIA_IP" "systemctl start autosmart" |
|
| 34 |
+sleep 3 |
|
| 35 |
+echo "✅ Service started" |
|
| 36 |
+echo "" |
|
| 37 |
+ |
|
| 38 |
+# 4. Verify service is running |
|
| 39 |
+echo "🔍 Verifying service status..." |
|
| 40 |
+if ssh "$TAPIA_USER@$TAPIA_IP" "systemctl is-active --quiet autosmart"; then |
|
| 41 |
+ echo "✅ autosmart service is running on tapia" |
|
| 42 |
+else |
|
| 43 |
+ echo "❌ autosmart service failed to start!" |
|
| 44 |
+ exit 1 |
|
| 45 |
+fi |
|
| 46 |
+echo "" |
|
| 47 |
+ |
|
| 48 |
+# 5. Check recent logs |
|
| 49 |
+echo "📋 Last 5 log entries from tapia:" |
|
| 50 |
+ssh "$TAPIA_USER@$TAPIA_IP" "tail -5 /var/log/syslog | grep autosmart" || true |
|
| 51 |
+echo "" |
|
| 52 |
+ |
|
| 53 |
+echo "✅ Deploy to tapia complete!" |
|
| 54 |
+echo "" |
|
| 55 |
+echo "Next: Verify data collection:" |
|
| 56 |
+echo " ssh root@ebony \"psql -h 192.168.2.102 -U postgres -d autosmart\"" |
|
| 57 |
+echo " SELECT node_id, COUNT(*) FROM smart_collection_events GROUP BY node_id;" |
|
@@ -213,38 +213,31 @@ sub process_device {
|
||
| 213 | 213 |
|
| 214 | 214 |
# Get or create HDD inventory entry |
| 215 | 215 |
my $hdd_id = get_or_create_hdd($dbh, $serial, $model, $device); |
| 216 |
- |
|
| 217 |
- # Check if we should store this reading |
|
| 216 |
+ |
|
| 217 |
+ # SCHEMA v2: Store complete reading via PostgreSQL function |
|
| 218 | 218 |
my $params_json = encode_json(\%smart_params); |
| 219 |
- |
|
| 220 |
- if (!$force_full && !$config->{node}{store_unchanged}) {
|
|
| 221 |
- # Check for recent identical reading |
|
| 222 |
- my $sth = $dbh->prepare("
|
|
| 223 |
- SELECT id FROM smart_readings |
|
| 224 |
- WHERE hdd_id = ? AND parameters_json = ? |
|
| 225 |
- AND timestamp > NOW() - INTERVAL '1 hour' |
|
| 226 |
- LIMIT 1 |
|
| 227 |
- "); |
|
| 228 |
- $sth->execute($hdd_id, $params_json); |
|
| 229 |
- |
|
| 230 |
- if ($sth->fetchrow_array()) {
|
|
| 231 |
- log_message(" Skipping unchanged parameters") if $debug;
|
|
| 232 |
- return; |
|
| 233 |
- } |
|
| 234 |
- } |
|
| 235 |
- |
|
| 236 |
- # Store SMART reading |
|
| 237 |
- my $reading_type = $force_full ? 'full' : 'differential'; |
|
| 238 |
- |
|
| 219 |
+ my $checksum = sha256_hex($params_json . ($temp || '')); |
|
| 220 |
+ |
|
| 239 | 221 |
my $sth = $dbh->prepare("
|
| 240 |
- INSERT INTO smart_readings (hdd_id, serial_number, device_path, node_id, timestamp, temperature, parameters_json, reading_type) |
|
| 241 |
- VALUES (?, ?, ?, ?, NOW(), ?, ?::jsonb, ?) |
|
| 242 |
- RETURNING id |
|
| 222 |
+ SELECT insert_collection_event(?, ?, ?, NOW(), ?, ?, ?, ?::jsonb) |
|
| 243 | 223 |
"); |
| 244 |
- |
|
| 245 |
- my $reading_id = $dbh->selectrow_array($sth, undef, $hdd_id, $serial, $device, $node_id, $temp || 0, $params_json, $reading_type); |
|
| 246 |
- |
|
| 247 |
- log_message(" ✓ SMART reading stored (ID: $reading_id, temp: " . ($temp || 0) . "°C, type: $reading_type)") if $debug;
|
|
| 224 |
+ |
|
| 225 |
+ my $event_id = $dbh->selectrow_array( |
|
| 226 |
+ $sth, undef, |
|
| 227 |
+ $hdd_id, |
|
| 228 |
+ $serial, |
|
| 229 |
+ $node_id, |
|
| 230 |
+ $temp || 0, |
|
| 231 |
+ 1, # collection_ok = true |
|
| 232 |
+ $checksum, |
|
| 233 |
+ $params_json |
|
| 234 |
+ ); |
|
| 235 |
+ |
|
| 236 |
+ if ($event_id) {
|
|
| 237 |
+ log_message(" ✓ SMART event stored (ID: $event_id, temp: " . ($temp || 0) . "°C, params: " . scalar(keys %smart_params) . ")") if $debug;
|
|
| 238 |
+ } else {
|
|
| 239 |
+ log_message(" ✗ Failed to store SMART event for $serial");
|
|
| 240 |
+ } |
|
| 248 | 241 |
} |
| 249 | 242 |
|
| 250 | 243 |
sub get_or_create_hdd {
|
@@ -0,0 +1,64 @@ |
||
| 1 |
+-- autoSMART — Rename v1 tables to archive |
|
| 2 |
+-- Run on-demand after validating collectors work with schema v2 |
|
| 3 |
+-- Date: 2026-05-20 |
|
| 4 |
+-- Safe: tables are read-only at this point |
|
| 5 |
+ |
|
| 6 |
+BEGIN; |
|
| 7 |
+ |
|
| 8 |
+-- Rename tables |
|
| 9 |
+ALTER TABLE IF EXISTS smart_readings RENAME TO smart_readings_archive_v1; |
|
| 10 |
+ALTER TABLE IF EXISTS smart_thresholds RENAME TO smart_thresholds_archive_v1; |
|
| 11 |
+ALTER TABLE IF EXISTS alert_history RENAME TO alert_history_archive_v1; |
|
| 12 |
+ |
|
| 13 |
+-- Rename sequences |
|
| 14 |
+ALTER SEQUENCE IF EXISTS smart_readings_id_seq RENAME TO smart_readings_archive_v1_id_seq; |
|
| 15 |
+ALTER SEQUENCE IF EXISTS smart_thresholds_id_seq RENAME TO smart_thresholds_archive_v1_id_seq; |
|
| 16 |
+ALTER SEQUENCE IF EXISTS alert_history_id_seq RENAME TO alert_history_archive_v1_id_seq; |
|
| 17 |
+ |
|
| 18 |
+-- Rename indexes |
|
| 19 |
+ALTER INDEX IF EXISTS smart_readings_pkey RENAME TO smart_readings_archive_v1_pkey; |
|
| 20 |
+ALTER INDEX IF EXISTS idx_smart_readings_hdd_id RENAME TO idx_smart_readings_archive_v1_hdd_id; |
|
| 21 |
+ALTER INDEX IF EXISTS idx_smart_readings_timestamp RENAME TO idx_smart_readings_archive_v1_timestamp; |
|
| 22 |
+ALTER INDEX IF EXISTS idx_smart_readings_serial RENAME TO idx_smart_readings_archive_v1_serial; |
|
| 23 |
+ALTER INDEX IF EXISTS idx_smart_readings_device_path RENAME TO idx_smart_readings_archive_v1_device_path; |
|
| 24 |
+ALTER INDEX IF EXISTS idx_smart_readings_type RENAME TO idx_smart_readings_archive_v1_type; |
|
| 25 |
+ALTER INDEX IF EXISTS idx_smart_readings_checksum RENAME TO idx_smart_readings_archive_v1_checksum; |
|
| 26 |
+ALTER INDEX IF EXISTS idx_smart_readings_previous RENAME TO idx_smart_readings_archive_v1_previous; |
|
| 27 |
+ALTER INDEX IF EXISTS idx_smart_readings_parameters RENAME TO idx_smart_readings_archive_v1_parameters; |
|
| 28 |
+ALTER INDEX IF EXISTS idx_smart_readings_changed_params RENAME TO idx_smart_readings_archive_v1_changed_params; |
|
| 29 |
+ |
|
| 30 |
+ALTER INDEX IF EXISTS smart_thresholds_pkey RENAME TO smart_thresholds_archive_v1_pkey; |
|
| 31 |
+ALTER INDEX IF EXISTS uq_parameter_name RENAME TO uq_archive_v1_parameter_name; |
|
| 32 |
+ |
|
| 33 |
+ALTER INDEX IF EXISTS alert_history_pkey RENAME TO alert_history_archive_v1_pkey; |
|
| 34 |
+ALTER INDEX IF EXISTS idx_alert_history_hdd_id RENAME TO idx_alert_history_archive_v1_hdd_id; |
|
| 35 |
+ALTER INDEX IF EXISTS idx_alert_history_sent_at RENAME TO idx_alert_history_archive_v1_sent_at; |
|
| 36 |
+ALTER INDEX IF EXISTS idx_alert_history_severity RENAME TO idx_alert_history_archive_v1_severity; |
|
| 37 |
+ALTER INDEX IF EXISTS idx_alert_history_serial RENAME TO idx_alert_history_archive_v1_serial; |
|
| 38 |
+ |
|
| 39 |
+-- Rename constraints |
|
| 40 |
+ALTER TABLE smart_readings_archive_v1 RENAME CONSTRAINT smart_readings_hdd_id_fkey TO smart_readings_archive_v1_hdd_id_fkey; |
|
| 41 |
+ALTER TABLE smart_readings_archive_v1 RENAME CONSTRAINT smart_readings_previous_reading_id_fkey TO smart_readings_archive_v1_previous_reading_id_fkey; |
|
| 42 |
+ALTER TABLE alert_history_archive_v1 RENAME CONSTRAINT alert_history_hdd_id_fkey TO alert_history_archive_v1_hdd_id_fkey; |
|
| 43 |
+ALTER TABLE alert_history_archive_v1 RENAME CONSTRAINT alert_history_related_reading_id_fkey TO alert_history_archive_v1_related_reading_id_fkey; |
|
| 44 |
+ALTER TABLE alert_history_archive_v1 RENAME CONSTRAINT alert_history_related_prediction_id_fkey TO alert_history_archive_v1_related_prediction_id_fkey; |
|
| 45 |
+ |
|
| 46 |
+COMMIT; |
|
| 47 |
+ |
|
| 48 |
+-- Summary |
|
| 49 |
+DO $$ |
|
| 50 |
+BEGIN |
|
| 51 |
+ RAISE NOTICE '✅ Archive rename complete!'; |
|
| 52 |
+ RAISE NOTICE 'Tables renamed:'; |
|
| 53 |
+ RAISE NOTICE ' • smart_readings → smart_readings_archive_v1'; |
|
| 54 |
+ RAISE NOTICE ' • smart_thresholds → smart_thresholds_archive_v1'; |
|
| 55 |
+ RAISE NOTICE ' • alert_history → alert_history_archive_v1'; |
|
| 56 |
+ RAISE NOTICE ''; |
|
| 57 |
+ RAISE NOTICE 'These tables are now read-only archives.'; |
|
| 58 |
+ RAISE NOTICE 'All new data flows through schema v2.'; |
|
| 59 |
+ RAISE NOTICE ''; |
|
| 60 |
+ RAISE NOTICE 'To delete archives (after 4-6 week validation):'; |
|
| 61 |
+ RAISE NOTICE ' DROP TABLE smart_readings_archive_v1 CASCADE;'; |
|
| 62 |
+ RAISE NOTICE ' DROP TABLE smart_thresholds_archive_v1 CASCADE;'; |
|
| 63 |
+ RAISE NOTICE ' DROP TABLE alert_history_archive_v1 CASCADE;'; |
|
| 64 |
+END $$; |
|
@@ -387,45 +387,61 @@ BEGIN |
||
| 387 | 387 |
END; |
| 388 | 388 |
$$ LANGUAGE plpgsql; |
| 389 | 389 |
|
| 390 |
-CREATE OR REPLACE FUNCTION enforce_data_retention(p_retain_months INTEGER DEFAULT 24) |
|
| 391 |
-RETURNS INTEGER AS $$ |
|
| 390 |
+-- On-demand data deletion: delete all data for a specific HDD (by serial_number) |
|
| 391 |
+-- RETENTION POLICY: On-demand only (no automatic cleanup) |
|
| 392 |
+-- Called from frontend when user requests to remove a drive from monitoring |
|
| 393 |
+CREATE OR REPLACE FUNCTION delete_hdd_data_by_serial( |
|
| 394 |
+ p_serial_number VARCHAR(100), |
|
| 395 |
+ p_keep_catalog BOOLEAN DEFAULT true |
|
| 396 |
+) RETURNS TABLE( |
|
| 397 |
+ deleted_values BIGINT, |
|
| 398 |
+ deleted_events BIGINT, |
|
| 399 |
+ deleted_catalog INTEGER |
|
| 400 |
+) AS $$ |
|
| 392 | 401 |
DECLARE |
| 393 |
- v_cutoff_date DATE; |
|
| 394 |
- v_dropped INTEGER := 0; |
|
| 395 |
- v_rec RECORD; |
|
| 402 |
+ v_hdd_id INTEGER; |
|
| 403 |
+ v_deleted_values BIGINT := 0; |
|
| 404 |
+ v_deleted_events BIGINT := 0; |
|
| 405 |
+ v_deleted_catalog INTEGER := 0; |
|
| 396 | 406 |
BEGIN |
| 397 |
- v_cutoff_date := (CURRENT_DATE - (p_retain_months || ' months')::INTERVAL)::DATE; |
|
| 407 |
+ -- Get HDD ID for this serial |
|
| 408 |
+ SELECT id INTO v_hdd_id FROM hdd_inventory WHERE serial_number = p_serial_number; |
|
| 398 | 409 |
|
| 399 |
- FOR v_rec IN |
|
| 400 |
- SELECT tablename |
|
| 401 |
- FROM pg_tables |
|
| 402 |
- WHERE tablename LIKE 'smart_param_values_%' |
|
| 403 |
- AND schemaname = 'public' |
|
| 404 |
- LOOP |
|
| 405 |
- DECLARE |
|
| 406 |
- v_year INTEGER; |
|
| 407 |
- v_month INTEGER; |
|
| 408 |
- v_parts TEXT[]; |
|
| 409 |
- BEGIN |
|
| 410 |
- v_parts := regexp_split_to_array(v_rec.tablename, '_'); |
|
| 411 |
- IF array_length(v_parts, 1) >= 5 THEN |
|
| 412 |
- v_year := v_parts[4]::INTEGER; |
|
| 413 |
- v_month := v_parts[5]::INTEGER; |
|
| 414 |
- |
|
| 415 |
- IF make_date(v_year, v_month, 1) < v_cutoff_date THEN |
|
| 416 |
- EXECUTE format('DROP TABLE IF EXISTS %I', v_rec.tablename);
|
|
| 417 |
- v_dropped := v_dropped + 1; |
|
| 418 |
- RAISE NOTICE 'Dropped old partition: %', v_rec.tablename; |
|
| 419 |
- END IF; |
|
| 420 |
- END IF; |
|
| 421 |
- END; |
|
| 422 |
- END LOOP; |
|
| 410 |
+ IF v_hdd_id IS NULL THEN |
|
| 411 |
+ RAISE NOTICE 'Serial % not found in inventory', p_serial_number; |
|
| 412 |
+ RETURN QUERY SELECT 0::BIGINT, 0::BIGINT, 0::INTEGER; |
|
| 413 |
+ RETURN; |
|
| 414 |
+ END IF; |
|
| 415 |
+ |
|
| 416 |
+ -- Delete from smart_param_values (cascade via event_id FK) |
|
| 417 |
+ DELETE FROM smart_param_values spv |
|
| 418 |
+ WHERE event_id IN ( |
|
| 419 |
+ SELECT id FROM smart_collection_events |
|
| 420 |
+ WHERE hdd_id = v_hdd_id |
|
| 421 |
+ ); |
|
| 422 |
+ GET DIAGNOSTICS v_deleted_values = ROW_COUNT; |
|
| 423 | 423 |
|
| 424 |
+ -- Delete from smart_collection_events |
|
| 424 | 425 |
DELETE FROM smart_collection_events |
| 425 |
- WHERE collected_at < v_cutoff_date; |
|
| 426 |
+ WHERE hdd_id = v_hdd_id OR serial_number = p_serial_number; |
|
| 427 |
+ GET DIAGNOSTICS v_deleted_events = ROW_COUNT; |
|
| 428 |
+ |
|
| 429 |
+ -- Optionally delete from hdd_inventory and catalog |
|
| 430 |
+ IF NOT p_keep_catalog THEN |
|
| 431 |
+ DELETE FROM smart_param_catalog spc |
|
| 432 |
+ WHERE NOT EXISTS ( |
|
| 433 |
+ SELECT 1 FROM smart_param_values |
|
| 434 |
+ WHERE param_id = spc.id |
|
| 435 |
+ ); |
|
| 436 |
+ GET DIAGNOSTICS v_deleted_catalog = ROW_COUNT; |
|
| 437 |
+ |
|
| 438 |
+ DELETE FROM hdd_inventory WHERE id = v_hdd_id; |
|
| 439 |
+ END IF; |
|
| 440 |
+ |
|
| 441 |
+ RAISE NOTICE 'Deleted % param values, % events for serial %', |
|
| 442 |
+ v_deleted_values, v_deleted_events, p_serial_number; |
|
| 426 | 443 |
|
| 427 |
- RETURN v_dropped; |
|
| 444 |
+ RETURN QUERY SELECT v_deleted_values, v_deleted_events, v_deleted_catalog; |
|
| 428 | 445 |
END; |
| 429 | 446 |
$$ LANGUAGE plpgsql; |
| 430 | 447 |
|
@@ -452,6 +468,6 @@ BEGIN |
||
| 452 | 468 |
RAISE NOTICE '✅ autoSMART Schema v2.0 deployed successfully!'; |
| 453 | 469 |
RAISE NOTICE 'New tables: smart_param_catalog, smart_collection_events, smart_param_values (partitioned)'; |
| 454 | 470 |
RAISE NOTICE 'Views: v_latest_param_values, v_drive_health_summary, v_param_trend, v_cluster_overview, v_smart_readings_compat'; |
| 455 |
- RAISE NOTICE 'Functions: upsert_param_catalog, insert_collection_event, create_monthly_partition, enforce_data_retention'; |
|
| 471 |
+ RAISE NOTICE 'Functions: upsert_param_catalog, insert_collection_event, create_monthly_partition, delete_hdd_data_by_serial'; |
|
| 456 | 472 |
RAISE NOTICE 'Next step: Run sql/migrate-v1-to-v2.sql to migrate existing data'; |
| 457 | 473 |
END $$; |