diff --git a/api/metrics.go b/api/metrics.go index 98c6f06b0..763b8c473 100644 --- a/api/metrics.go +++ b/api/metrics.go @@ -50,14 +50,6 @@ type ( Reason string } - PerformanceMetric struct { - Action string `json:"action"` - HostKey types.PublicKey `json:"hostKey"` - Origin string `json:"origin"` - Duration time.Duration `json:"duration"` - Timestamp TimeRFC3339 `json:"timestamp"` - } - PerformanceMetricsQueryOpts struct { Action string HostKey types.PublicKey diff --git a/internal/sql/migrations.go b/internal/sql/migrations.go index 9b98be300..c17473542 100644 --- a/internal/sql/migrations.go +++ b/internal/sql/migrations.go @@ -211,6 +211,12 @@ var ( return performMigration(ctx, tx, migrationsFs, dbIdentifier, "00016_account_owner", log) }, }, + { + ID: "00017_unix_ms", + Migrate: func(tx Tx) error { + return performMigration(ctx, tx, migrationsFs, dbIdentifier, "00017_unix_ms", log) + }, + }, } } MetricsMigrations = func(ctx context.Context, migrationsFs embed.FS, log *zap.SugaredLogger) []Migration { diff --git a/internal/test/e2e/cluster_test.go b/internal/test/e2e/cluster_test.go index c249e82bd..561f864a8 100644 --- a/internal/test/e2e/cluster_test.go +++ b/internal/test/e2e/cluster_test.go @@ -2695,7 +2695,7 @@ func TestHostScan(t *testing.T) { // fetch hosts again with the unix epoch timestamp which should only return // 1 host since that one hasn't been scanned yet toScan, err := b.HostsForScanning(context.Background(), api.HostsForScanningOptions{ - MaxLastScan: api.TimeRFC3339(time.Unix(0, 1)), + MaxLastScan: api.TimeRFC3339(time.UnixMilli(1)), }) tt.OK(err) if len(toScan) != 1 { diff --git a/stores/hostdb_test.go b/stores/hostdb_test.go index b4bba0dc1..ba3b2c1ac 100644 --- a/stores/hostdb_test.go +++ b/stores/hostdb_test.go @@ -430,7 +430,7 @@ func TestRecordScan(t *testing.T) { t.Fatal(err) } if host.Interactions != (api.HostInteractions{}) { - t.Fatal("mismatch") + t.Fatal("mismatch", cmp.Diff(host.Interactions, api.HostInteractions{})) } if host.Settings != (rhpv2.HostSettings{}) { t.Fatal("mismatch") @@ -487,7 +487,7 @@ func TestRecordScan(t *testing.T) { // We expect no uptime or downtime from only a single scan. uptime := time.Duration(0) downtime := time.Duration(0) - if host.Interactions.LastScan.UnixNano() != firstScanTime.UnixNano() { + if host.Interactions.LastScan.UnixMilli() != firstScanTime.UnixMilli() { t.Fatal("wrong time") } host.Interactions.LastScan = time.Time{} @@ -517,7 +517,7 @@ func TestRecordScan(t *testing.T) { host, err = ss.Host(ctx, hk) if err != nil { t.Fatal(err) - } else if host.Interactions.LastScan.UnixNano() != secondScanTime.UnixNano() { + } else if host.Interactions.LastScan.UnixMilli() != secondScanTime.UnixMilli() { t.Fatal("wrong time") } else if time.Now().After(host.PriceTable.Expiry) { t.Fatal("invalid expiry") @@ -555,7 +555,7 @@ func TestRecordScan(t *testing.T) { if err != nil { t.Fatal(err) } - if host.Interactions.LastScan.UnixNano() != thirdScanTime.UnixNano() { + if host.Interactions.LastScan.UnixMilli() != thirdScanTime.UnixMilli() { t.Fatal("wrong time") } host.Interactions.LastScan = time.Time{} diff --git a/stores/metrics.go b/stores/metrics.go index 62dbde8ce..2c5dd314b 100644 --- a/stores/metrics.go +++ b/stores/metrics.go @@ -40,14 +40,6 @@ func (s *SQLStore) ContractSetMetrics(ctx context.Context, start time.Time, n ui return } -func (s *SQLStore) PerformanceMetrics(ctx context.Context, start time.Time, n uint64, interval time.Duration, opts api.PerformanceMetricsQueryOpts) (metrics []api.PerformanceMetric, err error) { - err = s.dbMetrics.Transaction(ctx, func(tx sql.MetricsDatabaseTx) (txErr error) { - metrics, txErr = tx.PerformanceMetrics(ctx, start, n, interval, opts) - return - }) - return -} - func (s *SQLStore) RecordContractMetric(ctx context.Context, metrics ...api.ContractMetric) error { return s.dbMetrics.Transaction(ctx, func(tx sql.MetricsDatabaseTx) error { return tx.RecordContractMetric(ctx, metrics...) @@ -72,12 +64,6 @@ func (s *SQLStore) RecordContractSetMetric(ctx context.Context, metrics ...api.C }) } -func (s *SQLStore) RecordPerformanceMetric(ctx context.Context, metrics ...api.PerformanceMetric) error { - return s.dbMetrics.Transaction(ctx, func(tx sql.MetricsDatabaseTx) error { - return tx.RecordPerformanceMetric(ctx, metrics...) - }) -} - func (s *SQLStore) RecordWalletMetric(ctx context.Context, metrics ...api.WalletMetric) error { return s.dbMetrics.Transaction(ctx, func(tx sql.MetricsDatabaseTx) error { return tx.RecordWalletMetric(ctx, metrics...) diff --git a/stores/metrics_test.go b/stores/metrics_test.go index 9a9f7b71b..76852e3ed 100644 --- a/stores/metrics_test.go +++ b/stores/metrics_test.go @@ -431,91 +431,6 @@ func TestNormaliseTimestamp(t *testing.T) { } } -func TestPerformanceMetrics(t *testing.T) { - ss := newTestSQLStore(t, defaultTestSQLStoreConfig) - defer ss.Close() - - // Create metrics to query. - actions := []string{"download", "upload"} - hosts := []types.PublicKey{types.GeneratePrivateKey().PublicKey(), types.GeneratePrivateKey().PublicKey()} - origins := []string{"worker1", "worker2"} - durations := []time.Duration{time.Second, time.Hour} - times := []time.Time{time.UnixMilli(3), time.UnixMilli(1), time.UnixMilli(2)} - var i byte - for _, action := range actions { - for _, host := range hosts { - for _, origin := range origins { - for _, duration := range durations { - for _, recordedTime := range times { - if err := ss.RecordPerformanceMetric(context.Background(), api.PerformanceMetric{ - Action: action, - Timestamp: api.TimeRFC3339(recordedTime), - Duration: duration, - HostKey: host, - Origin: origin, - }); err != nil { - t.Fatal(err) - } - i++ - } - } - } - } - } - - assertMetrics := func(start time.Time, n uint64, interval time.Duration, opts api.PerformanceMetricsQueryOpts, expected int, cmp func(api.PerformanceMetric)) { - t.Helper() - metrics, err := ss.PerformanceMetrics(context.Background(), start, n, interval, opts) - if err != nil { - t.Fatal(err) - } - if len(metrics) != expected { - t.Fatalf("expected %v metrics, got %v", expected, len(metrics)) - } else if !sort.SliceIsSorted(metrics, func(i, j int) bool { - return time.Time(metrics[i].Timestamp).Before(time.Time(metrics[j].Timestamp)) - }) { - t.Fatal("expected metrics to be sorted by time") - } - for _, m := range metrics { - cmp(m) - } - } - - // Query without any filters. - start := time.UnixMilli(1) - assertMetrics(start, 3, time.Millisecond, api.PerformanceMetricsQueryOpts{}, 3, func(m api.PerformanceMetric) {}) - - // Filter by actions. - assertMetrics(start, 3, time.Millisecond, api.PerformanceMetricsQueryOpts{Action: actions[0]}, 3, func(m api.PerformanceMetric) { - if m.Action != actions[0] { - t.Fatalf("expected action to be %v, got %v", actions[0], m.Action) - } - }) - - // Filter by hosts. - assertMetrics(start, 3, time.Millisecond, api.PerformanceMetricsQueryOpts{HostKey: hosts[0]}, 3, func(m api.PerformanceMetric) { - if m.HostKey != hosts[0] { - t.Fatalf("expected hosts to be %v, got %v", hosts[0], m.HostKey) - } - }) - - // Filter by reporters. - assertMetrics(start, 3, time.Millisecond, api.PerformanceMetricsQueryOpts{Origin: origins[0]}, 3, func(m api.PerformanceMetric) { - if m.Origin != origins[0] { - t.Fatalf("expected origin to be %v, got %v", origins[0], m.Origin) - } - }) - - // Prune metrics - if err := ss.PruneMetrics(context.Background(), api.MetricPerformance, time.UnixMilli(3)); err != nil { - t.Fatal(err) - } else if metrics, err := ss.PerformanceMetrics(context.Background(), time.UnixMilli(1), 3, time.Millisecond, api.PerformanceMetricsQueryOpts{}); err != nil { - t.Fatal(err) - } else if len(metrics) != 1 { - t.Fatalf("expected 1 metric, got %v", len(metrics)) - } -} - func TestWalletMetrics(t *testing.T) { ss := newTestSQLStore(t, defaultTestSQLStoreConfig) defer ss.Close() diff --git a/stores/sql/database.go b/stores/sql/database.go index bc08865da..cae01caf5 100644 --- a/stores/sql/database.go +++ b/stores/sql/database.go @@ -426,9 +426,6 @@ type ( // time range and options. ContractSetMetrics(ctx context.Context, start time.Time, n uint64, interval time.Duration, opts api.ContractSetMetricsQueryOpts) ([]api.ContractSetMetric, error) - // PerformanceMetrics returns performance metrics for the given time range - PerformanceMetrics(ctx context.Context, start time.Time, n uint64, interval time.Duration, opts api.PerformanceMetricsQueryOpts) ([]api.PerformanceMetric, error) - // PruneMetrics deletes metrics of a certain type older than the given // cutoff time. PruneMetrics(ctx context.Context, metric string, cutoff time.Time) error @@ -445,9 +442,6 @@ type ( // RecordContractSetMetric records contract set metrics. RecordContractSetMetric(ctx context.Context, metrics ...api.ContractSetMetric) error - // RecordPerformanceMetric records performance metrics. - RecordPerformanceMetric(ctx context.Context, metrics ...api.PerformanceMetric) error - // RecordWalletMetric records wallet metrics. RecordWalletMetric(ctx context.Context, metrics ...api.WalletMetric) error diff --git a/stores/sql/main.go b/stores/sql/main.go index 655859bfc..557a5f666 100644 --- a/stores/sql/main.go +++ b/stores/sql/main.go @@ -670,7 +670,7 @@ func HostsForScanning(ctx context.Context, tx sql.Tx, maxLastScan time.Time, off } rows, err := tx.Query(ctx, "SELECT public_key, net_address FROM hosts WHERE last_scan < ? LIMIT ? OFFSET ?", - maxLastScan.UnixNano(), limit, offset) + UnixTimeMS(maxLastScan), limit, offset) if err != nil { return nil, fmt.Errorf("failed to fetch hosts for scanning: %w", err) } @@ -1751,7 +1751,7 @@ func RecordHostScans(ctx context.Context, tx sql.Tx, scans []api.HostScan) error now := time.Now() for _, scan := range scans { - scanTime := scan.Timestamp.UnixNano() + scanTime := scan.Timestamp.UnixMilli() _, err = stmt.Exec(ctx, scan.Success, // scanned scan.Success, // last_scan_success @@ -1827,7 +1827,7 @@ func RemoveOfflineHosts(ctx context.Context, tx sql.Tx, minRecentFailures uint64 FROM contracts INNER JOIN hosts h ON h.id = contracts.host_id WHERE recent_downtime >= ? AND recent_scan_failures >= ? - `, maxDownTime, minRecentFailures) + `, DurationMS(maxDownTime), minRecentFailures) if err != nil { return 0, fmt.Errorf("failed to fetch contracts: %w", err) } @@ -1851,7 +1851,7 @@ func RemoveOfflineHosts(ctx context.Context, tx sql.Tx, minRecentFailures uint64 // delete hosts res, err := tx.Exec(ctx, "DELETE FROM hosts WHERE recent_downtime >= ? AND recent_scan_failures >= ?", - maxDownTime, minRecentFailures) + DurationMS(maxDownTime), minRecentFailures) if err != nil { return 0, fmt.Errorf("failed to delete hosts: %w", err) } @@ -1909,7 +1909,7 @@ func QueryContracts(ctx context.Context, tx sql.Tx, whereExprs []string, whereAr SELECT c.fcid, c.renewed_from, c.contract_price, c.state, c.total_cost, c.proof_height, c.revision_height, c.revision_number, c.size, c.start_height, c.window_start, c.window_end, c.upload_spending, c.download_spending, c.fund_account_spending, c.delete_spending, c.list_spending, - COALESCE(cs.name, ""), h.net_address, h.public_key, h.settings->>'$.siamuxport' AS siamux_port + COALESCE(cs.name, ""), h.net_address, h.public_key, COALESCE(h.settings->>'$.siamuxport', "") AS siamux_port FROM contracts AS c INNER JOIN hosts h ON h.id = c.host_id LEFT JOIN contract_set_contracts csc ON csc.db_contract_id = c.id @@ -2134,8 +2134,8 @@ func SearchHosts(ctx context.Context, tx sql.Tx, autopilot, filterMode, usabilit var resolvedAddresses string err := rows.Scan(&hostID, &h.KnownSince, &h.LastAnnouncement, (*PublicKey)(&h.PublicKey), &h.NetAddress, (*PriceTable)(&h.PriceTable.HostPriceTable), &pte, - (*HostSettings)(&h.Settings), &h.Interactions.TotalScans, (*UnixTimeNS)(&h.Interactions.LastScan), &h.Interactions.LastScanSuccess, - &h.Interactions.SecondToLastScanSuccess, &h.Interactions.Uptime, &h.Interactions.Downtime, + (*HostSettings)(&h.Settings), &h.Interactions.TotalScans, (*UnixTimeMS)(&h.Interactions.LastScan), &h.Interactions.LastScanSuccess, + &h.Interactions.SecondToLastScanSuccess, (*DurationMS)(&h.Interactions.Uptime), (*DurationMS)(&h.Interactions.Downtime), &h.Interactions.SuccessfulInteractions, &h.Interactions.FailedInteractions, &h.Interactions.LostSectors, &h.Scanned, &resolvedAddresses, &h.Blocked, ) @@ -2547,7 +2547,7 @@ func scanWalletEvent(s Scanner) (wallet.Event, error) { var inflow, outflow Currency var edata []byte var etype string - var ts UnixTimeNS + var ts UnixTimeMS if err := s.Scan( &eventID, &blockID, diff --git a/stores/sql/metrics.go b/stores/sql/metrics.go index 6f6e5420f..966b091a4 100644 --- a/stores/sql/metrics.go +++ b/stores/sql/metrics.go @@ -89,7 +89,7 @@ func ContractPruneMetrics(ctx context.Context, tx sql.Tx, start time.Time, n uin &m.HostVersion, (*Unsigned64)(&m.Pruned), (*Unsigned64)(&m.Remaining), - &m.Duration, + (*DurationMS)(&m.Duration), ) if err != nil { err = fmt.Errorf("failed to scan contract prune metric: %w", err) @@ -144,29 +144,6 @@ func ContractSetMetrics(ctx context.Context, tx sql.Tx, start time.Time, n uint6 }) } -func PerformanceMetrics(ctx context.Context, tx sql.Tx, start time.Time, n uint64, interval time.Duration, opts api.PerformanceMetricsQueryOpts) ([]api.PerformanceMetric, error) { - return queryPeriods(ctx, tx, start, n, interval, opts, func(rows *sql.LoggedRows) (m api.PerformanceMetric, err error) { - var placeHolder int64 - var placeHolderTime time.Time - var timestamp UnixTimeMS - err = rows.Scan( - &placeHolder, - &placeHolderTime, - ×tamp, - &m.Action, - (*PublicKey)(&m.HostKey), - &m.Origin, - &m.Duration, - ) - if err != nil { - err = fmt.Errorf("failed to scan contract set metric: %w", err) - return - } - m.Timestamp = api.TimeRFC3339(normaliseTimestamp(start, interval, timestamp)) - return - }) -} - func PruneMetrics(ctx context.Context, tx sql.Tx, metric string, cutoff time.Time) error { if metric == "" { return errors.New("metric must be set") @@ -269,7 +246,7 @@ func RecordContractPruneMetric(ctx context.Context, tx sql.Tx, metrics ...api.Co metric.HostVersion, Unsigned64(metric.Pruned), Unsigned64(metric.Remaining), - metric.Duration, + (DurationMS)(metric.Duration), ) if err != nil { return fmt.Errorf("failed to insert contract prune metric: %w", err) @@ -337,34 +314,6 @@ func RecordContractSetMetric(ctx context.Context, tx sql.Tx, metrics ...api.Cont return nil } -func RecordPerformanceMetric(ctx context.Context, tx sql.Tx, metrics ...api.PerformanceMetric) error { - insertStmt, err := tx.Prepare(ctx, "INSERT INTO performance (created_at, timestamp, action, host, origin, duration) VALUES (?, ?, ?, ?, ?, ?)") - if err != nil { - return fmt.Errorf("failed to prepare statement to insert performance metric: %w", err) - } - defer insertStmt.Close() - - for _, metric := range metrics { - res, err := insertStmt.Exec(ctx, - time.Now().UTC(), - UnixTimeMS(metric.Timestamp), - metric.Action, - PublicKey(metric.HostKey), - metric.Origin, - metric.Duration, - ) - if err != nil { - return fmt.Errorf("failed to insert performance metric: %w", err) - } else if n, err := res.RowsAffected(); err != nil { - return fmt.Errorf("failed to get rows affected: %w", err) - } else if n == 0 { - return fmt.Errorf("failed to insert performance metric: no rows affected") - } - } - - return nil -} - func RecordWalletMetric(ctx context.Context, tx sql.Tx, metrics ...api.WalletMetric) error { insertStmt, err := tx.Prepare(ctx, "INSERT INTO wallets (created_at, timestamp, confirmed_lo, confirmed_hi, spendable_lo, spendable_hi, unconfirmed_lo, unconfirmed_hi, immature_hi, immature_lo) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") if err != nil { diff --git a/stores/sql/mysql/chain.go b/stores/sql/mysql/chain.go index 56d5ba340..4e5720c9e 100644 --- a/stores/sql/mysql/chain.go +++ b/stores/sql/mysql/chain.go @@ -102,7 +102,7 @@ func (c chainUpdateTx) WalletApplyIndex(index types.ChainIndex, created, spent [ e.Type, data, e.MaturityHeight, - ssql.UnixTimeNS(e.Timestamp), + ssql.UnixTimeMS(e.Timestamp), ); err != nil { return fmt.Errorf("failed to insert new event: %w", err) } diff --git a/stores/sql/mysql/metrics.go b/stores/sql/mysql/metrics.go index e7ef23813..8a64fa155 100644 --- a/stores/sql/mysql/metrics.go +++ b/stores/sql/mysql/metrics.go @@ -89,10 +89,6 @@ func (tx *MetricsDatabaseTx) ContractSetMetrics(ctx context.Context, start time. return ssql.ContractSetMetrics(ctx, tx, start, n, interval, opts) } -func (tx *MetricsDatabaseTx) PerformanceMetrics(ctx context.Context, start time.Time, n uint64, interval time.Duration, opts api.PerformanceMetricsQueryOpts) ([]api.PerformanceMetric, error) { - return ssql.PerformanceMetrics(ctx, tx, start, n, interval, opts) -} - func (tx *MetricsDatabaseTx) PruneMetrics(ctx context.Context, metric string, cutoff time.Time) error { return ssql.PruneMetrics(ctx, tx, metric, cutoff) } @@ -113,10 +109,6 @@ func (tx *MetricsDatabaseTx) RecordContractSetMetric(ctx context.Context, metric return ssql.RecordContractSetMetric(ctx, tx, metrics...) } -func (tx *MetricsDatabaseTx) RecordPerformanceMetric(ctx context.Context, metrics ...api.PerformanceMetric) error { - return ssql.RecordPerformanceMetric(ctx, tx, metrics...) -} - func (tx *MetricsDatabaseTx) RecordWalletMetric(ctx context.Context, metrics ...api.WalletMetric) error { return ssql.RecordWalletMetric(ctx, tx, metrics...) } diff --git a/stores/sql/mysql/migrations/main/migration_00017_unix_ms.sql b/stores/sql/mysql/migrations/main/migration_00017_unix_ms.sql new file mode 100644 index 000000000..dee60ba92 --- /dev/null +++ b/stores/sql/mysql/migrations/main/migration_00017_unix_ms.sql @@ -0,0 +1,4 @@ +UPDATE hosts SET hosts.last_scan = CAST(hosts.last_scan / 1000000 AS SIGNED); +UPDATE hosts SET hosts.uptime = CAST(hosts.uptime / 1000000 AS SIGNED); +UPDATE hosts SET hosts.downtime = CAST(hosts.downtime / 1000000 AS SIGNED); +UPDATE wallet_events SET wallet_events.timestamp = CAST(wallet_events.timestamp / 1000000 AS SIGNED); diff --git a/stores/sql/mysql/migrations/metrics/migration_00003_unix_ms.sql b/stores/sql/mysql/migrations/metrics/migration_00003_unix_ms.sql new file mode 100644 index 000000000..0960761d3 --- /dev/null +++ b/stores/sql/mysql/migrations/metrics/migration_00003_unix_ms.sql @@ -0,0 +1,2 @@ +UPDATE contract_prunes SET contract_prunes.timestamp = CAST(contract_prunes.timestamp / 1000000 AS SIGNED); +DROP TABLE IF EXISTS performance; diff --git a/stores/sql/mysql/migrations/metrics/schema.sql b/stores/sql/mysql/migrations/metrics/schema.sql index 7c4c27d6c..8dcb97769 100644 --- a/stores/sql/mysql/migrations/metrics/schema.sql +++ b/stores/sql/mysql/migrations/metrics/schema.sql @@ -86,23 +86,6 @@ CREATE TABLE `contracts` ( KEY `idx_contracts_fcid_timestamp` (`fcid`,`timestamp`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; --- dbPerformanceMetric -CREATE TABLE `performance` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `created_at` datetime(3) DEFAULT NULL, - `timestamp` bigint NOT NULL, - `action` varchar(191) NOT NULL, - `host` varbinary(32) NOT NULL, - `origin` varchar(191) NOT NULL, - `duration` bigint NOT NULL, - PRIMARY KEY (`id`), - KEY `idx_performance_host` (`host`), - KEY `idx_performance_origin` (`origin`), - KEY `idx_performance_duration` (`duration`), - KEY `idx_performance_timestamp` (`timestamp`), - KEY `idx_performance_action` (`action`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - -- dbWalletMetric CREATE TABLE `wallets` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, @@ -122,4 +105,4 @@ CREATE TABLE `wallets` ( KEY `idx_spendable` (`spendable_lo`,`spendable_hi`), KEY `idx_unconfirmed` (`unconfirmed_lo`,`unconfirmed_hi`), KEY `idx_wallets_immature` (`immature_lo`,`immature_hi`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; diff --git a/stores/sql/sqlite/chain.go b/stores/sql/sqlite/chain.go index a98b777a4..0a80e1a98 100644 --- a/stores/sql/sqlite/chain.go +++ b/stores/sql/sqlite/chain.go @@ -105,7 +105,7 @@ func (c chainUpdateTx) WalletApplyIndex(index types.ChainIndex, created, spent [ e.Type, data, e.MaturityHeight, - ssql.UnixTimeNS(e.Timestamp), + ssql.UnixTimeMS(e.Timestamp), ); err != nil { return fmt.Errorf("failed to insert new event: %w", err) } diff --git a/stores/sql/sqlite/metrics.go b/stores/sql/sqlite/metrics.go index df912d7c7..70281b114 100644 --- a/stores/sql/sqlite/metrics.go +++ b/stores/sql/sqlite/metrics.go @@ -88,10 +88,6 @@ func (tx *MetricsDatabaseTx) ContractSetMetrics(ctx context.Context, start time. return ssql.ContractSetMetrics(ctx, tx, start, n, interval, opts) } -func (tx *MetricsDatabaseTx) PerformanceMetrics(ctx context.Context, start time.Time, n uint64, interval time.Duration, opts api.PerformanceMetricsQueryOpts) ([]api.PerformanceMetric, error) { - return ssql.PerformanceMetrics(ctx, tx, start, n, interval, opts) -} - func (tx *MetricsDatabaseTx) PruneMetrics(ctx context.Context, metric string, cutoff time.Time) error { return ssql.PruneMetrics(ctx, tx, metric, cutoff) } @@ -112,10 +108,6 @@ func (tx *MetricsDatabaseTx) RecordContractSetMetric(ctx context.Context, metric return ssql.RecordContractSetMetric(ctx, tx, metrics...) } -func (tx *MetricsDatabaseTx) RecordPerformanceMetric(ctx context.Context, metrics ...api.PerformanceMetric) error { - return ssql.RecordPerformanceMetric(ctx, tx, metrics...) -} - func (tx *MetricsDatabaseTx) RecordWalletMetric(ctx context.Context, metrics ...api.WalletMetric) error { return ssql.RecordWalletMetric(ctx, tx, metrics...) } diff --git a/stores/sql/sqlite/migrations/main/migration_00017_unix_ms.sql b/stores/sql/sqlite/migrations/main/migration_00017_unix_ms.sql new file mode 100644 index 000000000..992897d7e --- /dev/null +++ b/stores/sql/sqlite/migrations/main/migration_00017_unix_ms.sql @@ -0,0 +1,4 @@ +UPDATE hosts SET last_scan = CAST(last_scan / 1000000 AS SIGNED); +UPDATE hosts SET uptime = CAST(uptime / 1000000 AS SIGNED); +UPDATE hosts SET downtime = CAST(downtime / 1000000 AS SIGNED); +UPDATE wallet_events SET timestamp = CAST(timestamp / 1000000 AS SIGNED); diff --git a/stores/sql/sqlite/migrations/metrics/migration_00003_unix_ms.sql b/stores/sql/sqlite/migrations/metrics/migration_00003_unix_ms.sql new file mode 100644 index 000000000..3538731fc --- /dev/null +++ b/stores/sql/sqlite/migrations/metrics/migration_00003_unix_ms.sql @@ -0,0 +1,2 @@ +UPDATE contract_prunes SET timestamp = CAST(timestamp / 1000000 AS SIGNED); +DROP TABLE IF EXISTS performance; diff --git a/stores/sql/sqlite/migrations/metrics/schema.sql b/stores/sql/sqlite/migrations/metrics/schema.sql index dfb8e3cf1..6741c72ac 100644 --- a/stores/sql/sqlite/migrations/metrics/schema.sql +++ b/stores/sql/sqlite/migrations/metrics/schema.sql @@ -37,14 +37,6 @@ CREATE INDEX `idx_contract_sets_churn_fc_id` ON `contract_sets_churn`(`fc_id`); CREATE INDEX `idx_contract_sets_churn_name` ON `contract_sets_churn`(`name`); CREATE INDEX `idx_contract_sets_churn_timestamp` ON `contract_sets_churn`(`timestamp`); --- dbPerformanceMetric -CREATE TABLE `performance` (`id` integer PRIMARY KEY AUTOINCREMENT,`created_at` datetime,`timestamp` BIGINT NOT NULL,`action` text NOT NULL,`host` blob NOT NULL,`origin` text NOT NULL,`duration` integer NOT NULL); -CREATE INDEX `idx_performance_duration` ON `performance`(`duration`); -CREATE INDEX `idx_performance_origin` ON `performance`(`origin`); -CREATE INDEX `idx_performance_host` ON `performance`(`host`); -CREATE INDEX `idx_performance_action` ON `performance`(`action`); -CREATE INDEX `idx_performance_timestamp` ON `performance`(`timestamp`); - -- dbWalletMetric CREATE TABLE `wallets` (`id` integer PRIMARY KEY AUTOINCREMENT,`created_at` datetime,`timestamp` BIGINT NOT NULL,`confirmed_lo` BIGINT NOT NULL,`confirmed_hi` BIGINT NOT NULL,`spendable_lo` BIGINT NOT NULL,`spendable_hi` BIGINT NOT NULL,`unconfirmed_lo` BIGINT NOT NULL,`unconfirmed_hi` BIGINT NOT NULL,`immature_lo` BIGINT NOT NULL,`immature_hi` BIGINT NOT NULL); CREATE INDEX `idx_unconfirmed` ON `wallets`(`unconfirmed_lo`,`unconfirmed_hi`); diff --git a/stores/sql/types.go b/stores/sql/types.go index 10cf76e42..ae71e16dd 100644 --- a/stores/sql/types.go +++ b/stores/sql/types.go @@ -43,7 +43,7 @@ type ( EncryptionKey object.EncryptionKey Uint64Str uint64 UnixTimeMS time.Time - UnixTimeNS time.Time + DurationMS time.Duration Unsigned64 uint64 ) @@ -66,7 +66,7 @@ var ( _ scannerValuer = (*PublicKey)(nil) _ scannerValuer = (*EncryptionKey)(nil) _ scannerValuer = (*UnixTimeMS)(nil) - _ scannerValuer = (*UnixTimeNS)(nil) + _ scannerValuer = (*DurationMS)(nil) _ scannerValuer = (*Unsigned64)(nil) ) @@ -204,6 +204,9 @@ func (hs *HostSettings) Scan(value interface{}) error { // Value returns a HostSettings value, implements driver.Valuer interface. func (hs HostSettings) Value() (driver.Value, error) { + if hs == (HostSettings{}) { + return []byte("{}"), nil + } return json.Marshal(hs) } @@ -218,6 +221,9 @@ func (pt *PriceTable) Scan(value interface{}) error { // Value returns a PriceTable value, implements driver.Valuer interface. func (pt PriceTable) Value() (driver.Value, error) { + if pt == (PriceTable{}) { + return []byte("{}"), nil + } return json.Marshal(pt) } @@ -331,8 +337,10 @@ func (u *UnixTimeMS) Scan(value interface{}) error { default: return fmt.Errorf("failed to unmarshal unixTimeMS value: %v %T", value, value) } - - *u = UnixTimeMS(time.UnixMilli(msec)) + *u = UnixTimeMS(time.Time{}) + if msec > 0 { + *u = UnixTimeMS(time.UnixMilli(msec)) + } return nil } @@ -342,34 +350,30 @@ func (u UnixTimeMS) Value() (driver.Value, error) { return time.Time(u).UnixMilli(), nil } -// Scan scan value into UnixTimeNS, implements sql.Scanner interface. -func (u *UnixTimeNS) Scan(value interface{}) error { - var nsec int64 +// Scan scan value into DurationMS, implements sql.Scanner interface. +func (d *DurationMS) Scan(value interface{}) error { + var msec int64 var err error switch value := value.(type) { case int64: - nsec = value + msec = value case []uint8: - nsec, err = strconv.ParseInt(string(value), 10, 64) + msec, err = strconv.ParseInt(string(value), 10, 64) if err != nil { - return fmt.Errorf("failed to unmarshal UnixTimeNS value: %v %T", value, value) + return fmt.Errorf("failed to unmarshal DurationMS value: %v %T", value, value) } default: - return fmt.Errorf("failed to unmarshal UnixTimeNS value: %v %T", value, value) + return fmt.Errorf("failed to unmarshal DurationMS value: %v %T", value, value) } - if nsec == 0 { - *u = UnixTimeNS{} - } else { - *u = UnixTimeNS(time.Unix(0, nsec)) - } + *d = DurationMS(msec) * DurationMS(time.Millisecond) return nil } -// Value returns a int64 value representing a unix timestamp in milliseconds, +// Value returns a int64 value representing a duration in milliseconds, // implements driver.Valuer interface. -func (u UnixTimeNS) Value() (driver.Value, error) { - return time.Time(u).UnixNano(), nil +func (d DurationMS) Value() (driver.Value, error) { + return time.Duration(d).Milliseconds(), nil } // Scan scan value into Uint64, implements sql.Scanner interface.