Files
WesandClaude Sonnet 4.5 27afedb32e Complete rewrite: SnapRAID SMART logger with BackBlaze algorithm
Fetches SMART data from Scrutiny's InfluxDB and calculates failure
probabilities using the exact BackBlaze tables from SnapRAID source.

Key features:
- Calculates both v12 (with attr 193) and v13 (without attr 193) algorithms
- v12 matches SnapRAID pre-v13.0 (includes Load Cycle Count)
- v13 matches modern SnapRAID v13.0+ (excludes Load Cycle Count)
- Stores both values for trending and comparison
- Correctly applies bit masks (16-bit for 187/188, 32-bit for others)
- Annualizes monthly rates and applies Poisson distribution
- Logs to PostgreSQL with device metadata

Solved the mystery: SnapRAID v12 uses attribute 193 which was removed
in v13.0. Load cycle count has massive impact on failure predictions
for some drives (80% vs 4% difference).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-07 06:55:39 -05:00

322 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SnapRAID SMART Logger - Investigation Notes
## Project Goal
Create a Python utility that:
1. Fetches SMART data from Scrutiny's InfluxDB
2. Calculates failure probability **exactly like SnapRAID does** (using BackBlaze algorithm)
3. Logs enhanced data to PostgreSQL
4. Eventually add alerts and automation
## Current Status: BLOCKED - Algorithm Mismatch
We successfully built the infrastructure but **cannot match SnapRAID's failure probability calculations**.
---
## The Mystery: 81% vs 4.63%
### What SnapRAID Reports for /dev/sdc (ZLW0A3QJ)
```
Temp Power Error FP Size
C OnDays Count TB Serial Device Disk
-----------------------------------------------------------------------
24 1968 0 81% 12.0 ZLW0A3QJ /dev/sdc d1
```
**Failure Probability: 81%**
### What smartctl -A Shows
```
ID# ATTRIBUTE_NAME VALUE WORST THRESH RAW_VALUE
5 Reallocated_Sector_Ct 100 100 010 0
187 Reported_Uncorrect 100 100 000 0
188 Command_Timeout 100 100 000 0
197 Current_Pending_Sector 100 100 000 0
198 Offline_Uncorrectable 100 100 000 0
```
**All critical attributes are PERFECT** (VALUE=100, RAW_VALUE=0)
### What Our BackBlaze Calculation Gives
Using the exact same BackBlaze tables extracted from SnapRAID source:
- With RAW_VALUE = 0: **Probability = 4.63%**
- With VALUE = 100: **Probability = 100%** (saturated, wrong)
**Neither matches SnapRAID's 81%!**
---
## What We Know About SnapRAID's Algorithm
### Source Code Analysis
From `cmdline/device.c`:
```c
static double smart_afr(uint64_t* smart, const char* model)
{
double afr = 0;
uint64_t mask32 = 0xffffffffU;
uint64_t mask16 = 0xffffU;
// Attribute 5: Reallocated Sectors (32-bit mask)
if (smart[5] != SMART_UNASSIGNED) {
double r = smart_afr_value(SMART_5_R, SMART_5_STEP, smart[5] & mask32);
if (afr < r) afr = r;
}
// Attribute 187: Reported Uncorrectable (16-bit mask)
if (smart[187] != SMART_UNASSIGNED) {
double r = smart_afr_value(SMART_187_R, SMART_187_STEP, smart[187] & mask16);
if (afr < r) afr = r;
}
// Attribute 188: Command Timeout (16-bit, skipped for Seagate)
if (strncmp(model, "ST", 2) != 0 && smart[188] != SMART_UNASSIGNED) {
double r = smart_afr_value(SMART_188_R, SMART_188_STEP, smart[188] & mask16);
if (afr < r) afr = r;
}
// Attribute 197: Current Pending Sectors (32-bit)
if (smart[197] != SMART_UNASSIGNED) {
double r = smart_afr_value(SMART_197_R, SMART_197_STEP, smart[197] & mask32);
if (afr < r) afr = r;
}
// Attribute 198: Offline Uncorrectable (32-bit)
if (smart[198] != SMART_UNASSIGNED) {
double r = smart_afr_value(SMART_198_R, SMART_198_STEP, smart[198] & mask32);
if (afr < r) afr = r;
}
return afr;
}
```
### Table Lookup Function
```c
static double smart_afr_value(double* tab, unsigned step, uint64_t value)
{
value /= step;
if (value >= SMART_MEASURES) value = SMART_MEASURES - 1;
return 365.0 / 30.0 * tab[value]; // Annualize monthly rate
}
```
### Final Probability Calculation
```c
// Poisson distribution: P(at least 1 failure) = 1 - e^(-AFR)
poisson_prob_n_or_more_failures(afr, 1) * 100
```
**Formula:** `P = (1 - e^(-AFR)) × 100`
---
## Key Findings
### 1. BackBlaze Tables Are Correct
We extracted all 5 tables (256 values each) from SnapRAID source:
- `SMART_5_R` (Reallocated Sectors)
- `SMART_187_R` (Reported Uncorrectable)
- `SMART_188_R` (Command Timeout)
- `SMART_197_R` (Current Pending Sectors)
- `SMART_198_R` (Offline Uncorrectable)
Tables match exactly - verified byte-for-byte.
### 2. Algorithm Is Correct
Our implementation:
1. ✅ Uses correct bit masks (16-bit for 187/188, 32-bit for 5/197/198)
2. ✅ Applies correct step size (all = 1)
3. ✅ Takes maximum AFR across attributes
4. ✅ Scales monthly to annual (365/30)
5. ✅ Applies Poisson: `1 - e^(-AFR)`
### 3. The Math Works Backwards
**If SnapRAID reports 81%, then:**
```
0.81 = 1 - e^(-AFR)
e^(-AFR) = 0.19
AFR = -ln(0.19) = 1.66
Monthly rate = 1.66 × (30/365) = 0.136
```
**Looking at SMART_187_R table:**
- `table[1] = 0.1287` → AFR = 1.565 → **P = 79%**
- `table[2] = 0.1579` → AFR = 1.920 → **P = 85%**
**Conclusion: SnapRAID is using an attribute value of 1 or 2 for one of the attributes!**
### 4. But We Can't Find That Value!
From both smartctl AND InfluxDB:
- RAW_VALUE = 0 for all 5 attributes
- VALUE (normalized) = 100 for all 5 attributes
- WORST = 100 for all 5 attributes
- No errors, no degradation visible
---
## Current Hypothesis
SnapRAID must be:
1. **Extracting a specific byte** from RAW_VALUE that contains 1-2?
2. **Using a different field** we haven't checked (FLAGS? WHEN_FAILED?)?
3. **Calculating a derived value** from multiple fields?
4. **Using a different version** of the algorithm than GitHub master?
---
## What We Need
### Immediate Next Step
Check if RAW_VALUE has byte-level data we're missing:
```bash
smartctl -A /dev/sdc | grep -E "^( 5|187|188|197|198)"
```
Need to see if there are parenthetical byte breakdowns like:
```
194 Temperature_Celsius 24 (0 15 0 0 0)
^^^^^^^^^^^^^ Individual bytes
```
### Alternative Approaches
1. **Run SnapRAID with debug/verbose mode** to see what values it reads
2. **Find the smartctl parsing code** in SnapRAID source
3. **Check SnapRAID version** - might be using different algorithm
4. **Contact SnapRAID author** if needed
---
## Project Files
### Core Implementation
- `smart_logger_v3.py` - Main logger (currently uses Scrutiny's failure rates)
- `backblaze_tables.py` - BackBlaze failure rate tables (256 values × 5 attributes)
- `schema_v2.sql` - PostgreSQL schema with devices + smart_metrics tables
- `populate_devices_final.sql` - Your 9 drives' metadata
### Debug Scripts
- `debug_smart_values.py` - Check InfluxDB raw SMART values
- `debug_scrutiny_failure_rate.py` - Check Scrutiny's calculated rates
- `debug_value_field.py` - Compare VALUE vs RAW_VALUE fields
- `test_backblaze_calc.py` - Test BackBlaze calculation with different inputs
### Setup
- `setup_database.sh` - PostgreSQL database creation
- `get_device_info.py` - Extract device WWNs from InfluxDB
- `requirements.txt` - Python dependencies
---
## Database Schema
### devices (manually populated)
```sql
device_wwn, device_path, serial_number, model, manufacturer,
capacity_bytes, size_tb, disk_role, notes
```
### smart_metrics (auto-populated by logger)
```sql
device_wwn, timestamp, temperature_celsius, power_on_days,
error_count, failure_probability_pct, smart_attributes (JSONB)
```
### Views
- `smart_latest` - Latest metrics per device with metadata
- `smart_high_risk` - Devices >50% failure probability
- `smart_summary` - SnapRAID-style summary table
---
## Database Connection Info
- **InfluxDB**: cyrion.c0smere.net:8086
- Org: scrutiny
- Bucket: metrics
- Token: (in .env)
- **PostgreSQL**: phlegethon.c0smere.net:5432
- Database: smart_monitoring
- User: smart_logger
- Password: saddog095
---
## Next Steps (After Solving Algorithm)
### Option 2 Features (After Option 1 Works)
1. **Alerts**
- Email/webhook when failure probability crosses thresholds
- Trend detection (rapidly increasing failure rate)
- Integration with monitoring systems
2. **SnapRAID Integration**
- Trigger scrub when high-risk drives detected
- Pre-scrub notifications
- Post-scrub verification
3. **Historical Analysis**
- Failure probability trends over time
- Predict when drives will cross risk thresholds
- Identify patterns across drive models/manufacturers
4. **Custom Queries**
- Drive health reports
- Lifetime statistics
- Comparison across drive families
---
## Comparison: SnapRAID vs Scrutiny vs Our Calculator
| Drive | SnapRAID | Scrutiny | Our Calc (raw=0) | Our Calc (val=100) |
|-------|----------|----------|------------------|---------------------|
| /dev/sdc (d1) | **81%** | 11% | 4.63% | 100% |
| /dev/sdb (d2) | 22% | 19% | 4.63% | 100% |
| /dev/sdd (d5) | 51% | 11% | 4.63% | 100% |
| /dev/sda (parity) | 49% | 16% | 4.63% | 100% |
| /dev/sdg (2-parity) | 30% | 12% | 4.63% | 100% |
| /dev/sdh (2-parity) | 4% | 5% | 4.63% | 100% |
| /dev/sde | 4% | 5% | 4.63% | 100% |
| /dev/sdf | 84% | 11% | 4.63% | 100% |
| /dev/sdi | 84% | 11% | 4.63% | 100% |
**Pattern:** SnapRAID shows high variance (4-84%), we show either 4.63% or 100%.
---
## Questions to Answer
1. **What exact value does SnapRAID read for attribute 5/187/188/197/198?**
2. **Is SnapRAID using RAW_VALUE, VALUE, WORST, or something else?**
3. **Could there be byte-level parsing we're missing?**
4. **What version of SnapRAID is running, and does it match GitHub master?**
5. **Is there a verbose mode to see what SnapRAID actually reads?**
---
## References
- [SnapRAID GitHub](https://github.com/amadvance/snapraid)
- [SnapRAID device.c](https://github.com/amadvance/snapraid/blob/master/cmdline/device.c)
- [Scrutiny GitHub](https://github.com/AnalogJ/scrutiny)
- [BackBlaze Hard Drive Stats](https://www.backblaze.com/cloud-storage/resources/hard-drive-test-data)
---
**Last Updated:** 2025-12-05
**Status:** 🔴 BLOCKED - Cannot match SnapRAID algorithm
**Blocker:** Unknown how SnapRAID calculates 81% with all attributes = 0/100