Newer Older
f16725e 3 months ago History
467 lines | 16.261kb
Bogdan Timofte authored 3 months ago
1
# autoSMART Database Documentation
2

            
3
## Overview
4

            
5
autoSMART uses PostgreSQL as its primary database for storing SMART data, HDD tracking information, predictions, and system configuration. The database is designed for multi-node cluster deployments with comprehensive HDD mobility tracking.
6

            
7
## Database Schema
8

            
9
### Core Tables
10

            
11
#### `hdd_inventory`
12
The central inventory table that tracks all HDDs across the cluster.
13

            
14
```sql
15
CREATE TABLE hdd_inventory (
16
    id                  SERIAL PRIMARY KEY,
17
    serial_number       VARCHAR(100) NOT NULL,
18
    model_name          VARCHAR(200) NOT NULL,
19
    firmware            VARCHAR(50),
20
    size_gb             INTEGER,
21
    manufacturer        VARCHAR(100),
22
    current_device_path VARCHAR(50),
23
    current_node_id     VARCHAR(50),
24
    current_slot        VARCHAR(20),
25
    madagascar_id       VARCHAR(100),
26
    first_seen          TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
27
    last_seen           TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
28
    status              VARCHAR(20) DEFAULT 'active',
29
    status_changed_at   TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
30
    notes               TEXT,
31
    created_at          TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
32
    updated_at          TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
33

            
34
    CONSTRAINT unique_hardware_id UNIQUE (serial_number, model_name)
35
);
36
```
37

            
38
**Key Features:**
39
- **Hardware-based identification**: Uses `serial_number` + `model_name` as unique constraint
40
- **Current location tracking**: `current_device_path`, `current_node_id` show where HDD is now
41
- **Lifecycle management**: `first_seen`, `last_seen`, `status` track HDD lifecycle
42
- **Madagascar integration**: `madagascar_id` field for cluster-specific identification
43

            
44
#### `hdd_presence`
45
Tracks HDD mobility across cluster nodes - records when HDDs are present on different nodes.
46

            
47
```sql
48
CREATE TABLE hdd_presence (
49
    id SERIAL PRIMARY KEY,
50
    serial_number VARCHAR(64) NOT NULL,
51
    node VARCHAR(64) NOT NULL,
52
    data_start TIMESTAMP NOT NULL,
53
    data_end TIMESTAMP NOT NULL,
54
    is_current BOOLEAN NOT NULL DEFAULT TRUE
55
);
56
```
57

            
58
**Key Features:**
59
- **Mobility tracking**: Records when HDDs move between nodes
60
- **Time-based records**: `data_start`/`data_end` define presence periods
61
- **Current vs Historic**: `is_current` flag marks active presence
62
- **Independent of inventory**: Works independently of `hdd_inventory` for pure mobility data
63

            
64
**Example Data:**
65
```sql
66
 id | serial_number  |   node    |         data_start         |          data_end          | is_current
67
----+----------------+-----------+----------------------------+----------------------------+------------
68
  4 | ZW60K01R       | ebony     | 2025-08-16 22:05:15.863971 | 2025-08-16 22:05:15.863971 | t
69
  3 | S2HSNXRH402205 | ebony     | 2025-08-16 22:05:15.109956 | 2025-08-16 22:05:15.109956 | t
70
  2 | ZW60K01R       | baobab    | 2025-08-16 21:47:13.873642 | 2025-08-16 22:03:31.052316 | f
71
  1 | S2HSNXRH402205 | tapia     | 2025-08-16 21:47:13.078524 | 2025-08-16 22:03:30.268985 | f
72
```
73

            
74
#### `smart_readings`
75
Stores SMART data readings with differential storage optimization.
76

            
77
```sql
78
CREATE TABLE smart_readings (
79
    id                   BIGSERIAL PRIMARY KEY,
80
    hdd_id               INTEGER REFERENCES hdd_inventory(id),
81
    serial_number        VARCHAR(100) NOT NULL,
82
    device_path          VARCHAR(50),
83
    node_id              VARCHAR(50),
84
    timestamp            TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
85
    collection_ok        BOOLEAN DEFAULT true,
86
    temperature          INTEGER,
87
    parameters_json      JSONB,
88
    reading_type         VARCHAR(20) DEFAULT 'full',
89
    changes_detected     BOOLEAN DEFAULT true,
90
    changed_parameters   JSONB,
91
    previous_reading_id  INTEGER REFERENCES smart_readings(id),
92
    checksum             VARCHAR(64)
93
);
94
```
95

            
96
**Reading Types:**
97
- `baseline`: First reading for an HDD
98
- `full`: Complete parameter set (forced by time interval)
99
- `differential`: Only changed parameters (optimization)
100
- `skipped`: No changes detected
101

            
102
**Key Features:**
103
- **Differential storage**: Only stores changes to reduce data volume
104
- **Full context**: Links to `hdd_inventory` and includes node information
105
- **Change tracking**: `previous_reading_id` creates reading chains
106
- **JSONB parameters**: Flexible storage for SMART attributes
107

            
108
#### `predictions`
109
AI-generated failure predictions and analysis.
110

            
111
```sql
112
CREATE TABLE predictions (
113
    id                    SERIAL PRIMARY KEY,
114
    hdd_id                INTEGER REFERENCES hdd_inventory(id),
115
    serial_number         VARCHAR(100) NOT NULL,
116
    device_path           VARCHAR(50),
117
    timestamp             TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
118
    risk_level            VARCHAR(20),
119
    failure_probability   DECIMAL(5,4),
120
    predicted_failure_date DATE,
121
    confidence_score      DECIMAL(5,4),
122
    analysis_summary      TEXT,
123
    recommendations       JSONB,
124
    openai_response       JSONB,
125
    created_at            TIMESTAMP WITH TIME ZONE DEFAULT NOW()
126
);
127
```
128

            
129
#### `alert_history`
130
Tracks all alerts sent about HDD issues.
131

            
132
```sql
133
CREATE TABLE alert_history (
134
    id              SERIAL PRIMARY KEY,
135
    hdd_id          INTEGER REFERENCES hdd_inventory(id),
136
    serial_number   VARCHAR(100) NOT NULL,
137
    alert_type      VARCHAR(50),
138
    severity        VARCHAR(20),
139
    message         TEXT,
140
    sent_at         TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
141
    sent_to         TEXT,
142
    delivery_status VARCHAR(20) DEFAULT 'pending',
143
    related_reading_id BIGINT REFERENCES smart_readings(id),
144
    related_prediction_id INTEGER REFERENCES predictions(id)
145
);
146
```
147

            
148
### Configuration Tables
149

            
150
#### `system_config`
151
Global system configuration parameters.
152

            
153
```sql
154
CREATE TABLE system_config (
155
    id          SERIAL PRIMARY KEY,
156
    config_key  VARCHAR(100) UNIQUE NOT NULL,
157
    value       TEXT,
158
    description TEXT,
159
    created_at  TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
160
    updated_at  TIMESTAMP WITH TIME ZONE DEFAULT NOW()
161
);
162
```
163

            
164
**Default Configuration:**
165
- `collection_interval_seconds`: SMART data collection frequency
166
- `differential_storage_enabled`: Enable/disable storage optimization
167
- `forced_storage_interval_hours`: Force full readings periodically
168
- `critical_parameter_force_store`: Always store critical changes
169
- `temperature_change_threshold`: Temperature delta for storage
170

            
171
#### `smart_thresholds`
172
SMART parameter warning and critical thresholds.
173

            
174
```sql
175
CREATE TABLE smart_thresholds (
176
    id                SERIAL PRIMARY KEY,
177
    parameter_name    VARCHAR(100) NOT NULL,
178
    warning_threshold NUMERIC,
179
    critical_threshold NUMERIC,
180
    weight            NUMERIC DEFAULT 1.0,
181
    enabled           BOOLEAN DEFAULT true,
182
    description       TEXT,
183
    created_at        TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
184
    updated_at        TIMESTAMP WITH TIME ZONE DEFAULT NOW()
185
);
186
```
187

            
188
## Views
189

            
190
### `smart_readings_reconstructed`
191
Reconstructs complete SMART data from differential storage.
192

            
193
```sql
194
CREATE VIEW smart_readings_reconstructed AS
195
WITH RECURSIVE reading_chain AS (
196
    -- Base case: get baseline readings
197
    SELECT id, hdd_id, serial_number, timestamp,
198
           parameters_json, temperature, reading_type,
199
           previous_reading_id, 1 as chain_level
200
    FROM smart_readings
201
    WHERE reading_type IN ('baseline', 'full')
202

            
203
    UNION ALL
204

            
205
    -- Recursive case: follow the chain of differential readings
206
    SELECT sr.id, sr.hdd_id, sr.serial_number, sr.timestamp,
207
           COALESCE(rc.parameters_json, '{}'::jsonb) || sr.parameters_json as parameters_json,
208
           COALESCE(sr.temperature, rc.temperature) as temperature,
209
           sr.reading_type, sr.previous_reading_id,
210
           rc.chain_level + 1
211
    FROM smart_readings sr
212
    JOIN reading_chain rc ON sr.previous_reading_id = rc.id
213
    WHERE sr.reading_type = 'differential'
214
)
215
SELECT id, hdd_id, serial_number, timestamp,
216
       parameters_json, temperature, reading_type, chain_level
217
FROM reading_chain;
218
```
219

            
220
### `latest_smart_readings`
221
Current SMART status for all active drives.
222

            
223
```sql
224
CREATE VIEW latest_smart_readings AS
225
SELECT DISTINCT ON (sr.hdd_id)
226
    sr.id, sr.hdd_id, sr.serial_number, sr.timestamp,
227
    sr.parameters_json, sr.temperature,
228
    hi.model_name, hi.manufacturer, hi.size_gb,
229
    hi.current_device_path, hi.current_node_id
230
FROM smart_readings_reconstructed sr
231
JOIN hdd_inventory hi ON sr.hdd_id = hi.id
232
ORDER BY sr.hdd_id, sr.timestamp DESC;
233
```
234

            
235
### `drive_health_summary`
236
Comprehensive health overview for all drives.
237

            
238
```sql
239
CREATE VIEW drive_health_summary AS
240
SELECT
241
    hi.id as hdd_id, hi.serial_number, hi.model_name,
242
    hi.manufacturer, hi.current_device_path, hi.current_node_id,
243
    hi.status, lsr.timestamp as last_reading, lsr.temperature,
244
    p.risk_level, p.failure_probability, p.predicted_failure_date,
245
    EXTRACT(EPOCH FROM (NOW() - lsr.timestamp))/3600 as hours_since_last_reading
246
FROM hdd_inventory hi
247
LEFT JOIN latest_smart_readings lsr ON hi.id = lsr.hdd_id
248
LEFT JOIN LATERAL (
249
    SELECT risk_level, failure_probability, predicted_failure_date
250
    FROM predictions
251
    WHERE hdd_id = hi.id
252
    ORDER BY timestamp DESC
253
    LIMIT 1
254
) p ON true
255
WHERE hi.status = 'active';
256
```
257

            
258
## Functions
259

            
260
### `update_hdd_presence()`
261
Manages HDD presence tracking when a drive is detected on a node.
262

            
263
```sql
264
CREATE OR REPLACE FUNCTION update_hdd_presence(
265
    p_serial_number VARCHAR(64),
266
    p_node VARCHAR(64)
267
) RETURNS VOID AS $$
268
BEGIN
269
    -- Mark all previous presence records for this serial as historic
270
    UPDATE hdd_presence
271
    SET is_current = FALSE
272
    WHERE serial_number = p_serial_number AND is_current = TRUE AND node <> p_node;
273

            
274
    -- Check if there's already a current presence for this serial/node
275
    IF EXISTS (SELECT 1 FROM hdd_presence WHERE serial_number = p_serial_number AND node = p_node AND is_current = TRUE) THEN
276
        -- Update data_end for existing current presence
277
        UPDATE hdd_presence
278
        SET data_end = NOW()
279
        WHERE serial_number = p_serial_number AND node = p_node AND is_current = TRUE;
280
    ELSE
281
        -- Create new presence record
282
        INSERT INTO hdd_presence (serial_number, node, data_start, data_end, is_current)
283
        VALUES (p_serial_number, p_node, NOW(), NOW(), TRUE);
284
    END IF;
285
END;
286
$$ LANGUAGE plpgsql;
287
```
288

            
289
### `should_store_smart_reading()`
290
Determines if a SMART reading should be stored based on differential storage logic.
291

            
292
```sql
293
CREATE OR REPLACE FUNCTION should_store_smart_reading(
294
    p_hdd_id INTEGER,
295
    p_parameters_json JSONB,
296
    p_checksum VARCHAR(64),
297
    p_timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW()
298
) RETURNS TABLE(
299
    should_store BOOLEAN,
300
    reading_type VARCHAR(20),
301
    changes_detected BOOLEAN,
302
    changed_parameters JSONB,
303
    previous_reading_id INTEGER
304
) AS $$
305
-- Function implementation handles:
306
-- - Differential storage enabled/disabled
307
-- - Checksum-based change detection
308
-- - Force intervals for full readings
309
-- - Reading type determination
310
$$;
311
```
312

            
313
## Indexes
314

            
315
### Performance Indexes
316
```sql
317
-- hdd_inventory indexes
318
CREATE INDEX idx_hdd_inventory_device_path ON hdd_inventory(current_device_path);
319
CREATE INDEX idx_hdd_inventory_node ON hdd_inventory(current_node_id);
320
CREATE INDEX idx_hdd_inventory_status ON hdd_inventory(status);
321
CREATE INDEX idx_hdd_inventory_last_seen ON hdd_inventory(last_seen);
322

            
323
-- hdd_presence indexes
324
CREATE INDEX idx_hdd_presence_serial_current ON hdd_presence(serial_number, is_current);
325
CREATE INDEX idx_hdd_presence_node ON hdd_presence(node);
326
CREATE INDEX idx_hdd_presence_data_end ON hdd_presence(data_end DESC);
327

            
328
-- smart_readings indexes
329
CREATE INDEX idx_smart_readings_hdd_id ON smart_readings(hdd_id);
330
CREATE INDEX idx_smart_readings_timestamp ON smart_readings(timestamp DESC);
331
CREATE INDEX idx_smart_readings_serial ON smart_readings(serial_number);
332
CREATE INDEX idx_smart_readings_device_path ON smart_readings(device_path);
333
CREATE INDEX idx_smart_readings_type ON smart_readings(reading_type);
334
CREATE INDEX idx_smart_readings_checksum ON smart_readings(checksum);
335
CREATE INDEX idx_smart_readings_previous ON smart_readings(previous_reading_id);
336

            
337
-- JSONB indexes for flexible queries
338
CREATE INDEX idx_smart_readings_parameters ON smart_readings USING GIN (parameters_json);
339
CREATE INDEX idx_smart_readings_changed_params ON smart_readings USING GIN (changed_parameters);
340
```
341

            
342
## Data Flow
343

            
344
### Collection Process
345
1. **Device Discovery**: Collector scans `/dev/sd*` and `/dev/nvme*` devices
346
2. **SMART Reading**: Uses `smartctl` to extract device information and parameters
347
3. **HDD Registration**: `get_or_create_hdd()` adds new devices to `hdd_inventory`
348
4. **Presence Tracking**: `update_hdd_presence()` records current node location
349
5. **Data Storage**: Stores SMART readings with differential optimization
350
6. **Change Detection**: Uses checksums to detect parameter changes
351

            
352
### Mobility Tracking
353
1. **HDD Detected**: When HDD is found on a new node
354
2. **Historic Records**: Previous presence records marked `is_current = FALSE`
355
3. **New Presence**: New record created with `is_current = TRUE`
356
4. **Timeline**: Complete history maintained with `data_start`/`data_end` timestamps
357

            
358
### Query Examples
359

            
360
#### Find HDD History
361
```sql
362
SELECT serial_number, node, data_start, data_end, is_current
363
FROM hdd_presence
364
WHERE serial_number = 'ZW60K01R'
365
ORDER BY data_start DESC;
366
```
367

            
368
#### Current HDD Locations
369
```sql
370
SELECT h.serial_number, h.model_name, p.node, h.current_device_path
371
FROM hdd_inventory h
372
JOIN hdd_presence p ON h.serial_number = p.serial_number
373
WHERE p.is_current = TRUE;
374
```
375

            
376
#### SMART Parameter Trends
377
```sql
378
SELECT timestamp,
379
       parameters_json->>'Power_On_Hours' as power_hours,
380
       parameters_json->>'Temperature_Celsius' as temp,
381
       temperature
382
FROM smart_readings_reconstructed
383
WHERE serial_number = 'ZW60K01R'
384
ORDER BY timestamp DESC
385
LIMIT 10;
386
```
387

            
388
#### Health Summary
389
```sql
390
SELECT * FROM drive_health_summary
391
WHERE current_node_id = 'ebony';
392
```
393

            
394
## Troubleshooting
395

            
396
### Common Issues
397

            
398
#### 1. Node ID Mismatch
399
**Problem**: HDD presence shows wrong node name
400
**Cause**: Deploy script used local hostname instead of target node name
401
**Solution**: Deploy script now correctly determines target node name from `cluster.json`
402

            
403
#### 2. Empty hdd_presence Table
404
**Problem**: No mobility tracking data
405
**Causes**:
406
- SMART parameter parsing regex incompatible with new smartctl format
407
- Missing database sequence permissions
408
- Incomplete smart_readings INSERT statements
409

            
410
**Solutions**:
411
- Updated regex to support both old and new smartctl formats
412
- Added sequence permissions: `GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO autosmart;`
413
- Fixed INSERT to include all required fields
414

            
415
#### 3. Differential Storage Issues
416
**Problem**: Too much or too little data stored
417
**Configuration**: Adjust in `system_config` table:
418
```sql
419
UPDATE system_config SET value = 'false' WHERE config_key = 'differential_storage_enabled';
420
UPDATE system_config SET value = '12' WHERE config_key = 'forced_storage_interval_hours';
421
```
422

            
423
## Permissions
424

            
425
### Database User Setup
426
```sql
427
-- Create autosmart user
428
CREATE USER autosmart WITH PASSWORD 'autoSMART2025!';
429

            
430
-- Grant permissions
431
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO autosmart;
432
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO autosmart;
433
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO autosmart;
434
```
435

            
436
### Sequence Permissions
437
```sql
438
-- Required for INSERT operations with SERIAL columns
439
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO autosmart;
440
```
441

            
442
## Maintenance
443

            
444
### Regular Tasks
445
1. **Monitor disk usage**: SMART readings table grows over time
446
2. **Archive old data**: Consider archiving readings older than 1 year
447
3. **Index maintenance**: REINDEX periodically for performance
448
4. **Backup**: Regular PostgreSQL backups recommended
449

            
450
### Performance Monitoring
451
```sql
452
-- Table sizes
453
SELECT schemaname, tablename,
454
       pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size
455
FROM pg_tables
456
WHERE schemaname = 'public'
457
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
458

            
459
-- Recent activity
460
SELECT COUNT(*) as readings_today
461
FROM smart_readings
462
WHERE timestamp > CURRENT_DATE;
463

            
464
SELECT COUNT(*) as active_drives
465
FROM hdd_inventory
466
WHERE status = 'active';
467
```