2020-10-28 15:28:06 +00:00
|
|
|
// Copyright (C) 2020 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package metabase
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
|
|
|
|
"storj.io/common/storj"
|
|
|
|
"storj.io/common/uuid"
|
|
|
|
)
|
|
|
|
|
|
|
|
// RawObject defines the full object that is stored in the database. It should be rarely used directly.
|
|
|
|
type RawObject struct {
|
|
|
|
ObjectStream
|
|
|
|
|
|
|
|
CreatedAt time.Time
|
|
|
|
ExpiresAt *time.Time
|
|
|
|
|
|
|
|
Status ObjectStatus
|
|
|
|
SegmentCount int32
|
|
|
|
|
2021-10-12 14:37:12 +01:00
|
|
|
EncryptedMetadataNonce []byte
|
2020-11-16 16:46:47 +00:00
|
|
|
EncryptedMetadata []byte
|
|
|
|
EncryptedMetadataEncryptedKey []byte
|
2020-10-28 15:28:06 +00:00
|
|
|
|
2021-04-09 09:24:18 +01:00
|
|
|
// TotalPlainSize is 0 for a migrated object.
|
2020-11-24 12:29:16 +00:00
|
|
|
TotalPlainSize int64
|
2020-10-28 15:28:06 +00:00
|
|
|
TotalEncryptedSize int64
|
2021-04-09 09:24:18 +01:00
|
|
|
// FixedSegmentSize is 0 for a migrated object.
|
|
|
|
FixedSegmentSize int32
|
2020-10-28 15:28:06 +00:00
|
|
|
|
|
|
|
Encryption storj.EncryptionParameters
|
|
|
|
|
|
|
|
// ZombieDeletionDeadline defines when the pending raw object should be deleted from the database.
|
|
|
|
// This is as a safeguard against objects that failed to upload and the client has not indicated
|
|
|
|
// whether they want to continue uploading or delete the already uploaded data.
|
|
|
|
ZombieDeletionDeadline *time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
// RawSegment defines the full segment that is stored in the database. It should be rarely used directly.
|
|
|
|
type RawSegment struct {
|
|
|
|
StreamID uuid.UUID
|
|
|
|
Position SegmentPosition
|
|
|
|
|
2021-08-05 00:56:50 +01:00
|
|
|
CreatedAt time.Time // non-nillable
|
2021-03-17 09:52:26 +00:00
|
|
|
RepairedAt *time.Time
|
2021-06-05 18:07:20 +01:00
|
|
|
ExpiresAt *time.Time
|
2021-03-12 17:43:30 +00:00
|
|
|
|
2020-10-28 15:28:06 +00:00
|
|
|
RootPieceID storj.PieceID
|
2021-10-12 14:37:12 +01:00
|
|
|
EncryptedKeyNonce []byte
|
2020-10-28 15:28:06 +00:00
|
|
|
EncryptedKey []byte
|
|
|
|
|
|
|
|
EncryptedSize int32 // size of the whole segment (not a piece)
|
2021-04-09 09:24:18 +01:00
|
|
|
// PlainSize is 0 for a migrated object.
|
|
|
|
PlainSize int32
|
|
|
|
// PlainOffset is 0 for a migrated object.
|
2020-10-28 15:28:06 +00:00
|
|
|
PlainOffset int64
|
2021-03-25 07:53:10 +00:00
|
|
|
EncryptedETag []byte
|
2020-10-28 15:28:06 +00:00
|
|
|
|
|
|
|
Redundancy storj.RedundancyScheme
|
|
|
|
|
|
|
|
InlineData []byte
|
|
|
|
Pieces Pieces
|
2021-10-27 09:50:27 +01:00
|
|
|
|
|
|
|
Placement storj.PlacementConstraint
|
2020-10-28 15:28:06 +00:00
|
|
|
}
|
|
|
|
|
2022-02-17 23:26:03 +00:00
|
|
|
// RawCopy contains a copy that is stored in the database.
|
|
|
|
type RawCopy struct {
|
|
|
|
StreamID uuid.UUID
|
|
|
|
AncestorStreamID uuid.UUID
|
|
|
|
}
|
|
|
|
|
2020-10-28 15:28:06 +00:00
|
|
|
// RawState contains full state of a table.
|
|
|
|
type RawState struct {
|
2023-11-15 14:11:26 +00:00
|
|
|
Objects []RawObject
|
|
|
|
Segments []RawSegment
|
2020-10-28 15:28:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TestingGetState returns the state of the database.
|
|
|
|
func (db *DB) TestingGetState(ctx context.Context) (_ *RawState, err error) {
|
|
|
|
state := &RawState{}
|
|
|
|
|
|
|
|
state.Objects, err = db.testingGetAllObjects(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.New("GetState: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
state.Segments, err = db.testingGetAllSegments(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.New("GetState: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return state, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestingDeleteAll deletes all objects and segments from the database.
|
|
|
|
func (db *DB) TestingDeleteAll(ctx context.Context) (err error) {
|
|
|
|
_, err = db.db.ExecContext(ctx, `
|
2023-07-24 16:45:32 +01:00
|
|
|
WITH ignore_full_scan_for_test AS (SELECT 1) DELETE FROM objects;
|
|
|
|
WITH ignore_full_scan_for_test AS (SELECT 1) DELETE FROM segments;
|
|
|
|
WITH ignore_full_scan_for_test AS (SELECT 1) DELETE FROM node_aliases;
|
|
|
|
WITH ignore_full_scan_for_test AS (SELECT 1) SELECT setval('node_alias_seq', 1, false);
|
2020-10-28 15:28:06 +00:00
|
|
|
`)
|
2021-02-08 09:33:45 +00:00
|
|
|
db.aliasCache = NewNodeAliasCache(db)
|
2020-10-28 15:28:06 +00:00
|
|
|
return Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// testingGetAllObjects returns the state of the database.
|
|
|
|
func (db *DB) testingGetAllObjects(ctx context.Context) (_ []RawObject, err error) {
|
|
|
|
objs := []RawObject{}
|
|
|
|
|
2021-07-28 14:44:22 +01:00
|
|
|
rows, err := db.db.QueryContext(ctx, `
|
2023-07-24 16:45:32 +01:00
|
|
|
WITH ignore_full_scan_for_test AS (SELECT 1)
|
2020-10-29 18:10:46 +00:00
|
|
|
SELECT
|
2020-10-28 15:28:06 +00:00
|
|
|
project_id, bucket_name, object_key, version, stream_id,
|
|
|
|
created_at, expires_at,
|
|
|
|
status, segment_count,
|
2020-11-16 16:46:47 +00:00
|
|
|
encrypted_metadata_nonce, encrypted_metadata, encrypted_metadata_encrypted_key,
|
2020-11-24 12:29:16 +00:00
|
|
|
total_plain_size, total_encrypted_size, fixed_segment_size,
|
2020-10-28 15:28:06 +00:00
|
|
|
encryption,
|
|
|
|
zombie_deletion_deadline
|
|
|
|
FROM objects
|
2020-10-29 18:10:46 +00:00
|
|
|
ORDER BY project_id ASC, bucket_name ASC, object_key ASC, version ASC
|
2020-10-28 15:28:06 +00:00
|
|
|
`)
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.New("testingGetAllObjects query: %w", err)
|
|
|
|
}
|
|
|
|
defer func() { err = errs.Combine(err, rows.Close()) }()
|
|
|
|
for rows.Next() {
|
|
|
|
var obj RawObject
|
|
|
|
err := rows.Scan(
|
|
|
|
&obj.ProjectID,
|
|
|
|
&obj.BucketName,
|
|
|
|
&obj.ObjectKey,
|
|
|
|
&obj.Version,
|
|
|
|
&obj.StreamID,
|
|
|
|
|
|
|
|
&obj.CreatedAt,
|
|
|
|
&obj.ExpiresAt,
|
|
|
|
|
|
|
|
&obj.Status, // TODO: fix encoding
|
|
|
|
&obj.SegmentCount,
|
|
|
|
|
|
|
|
&obj.EncryptedMetadataNonce,
|
|
|
|
&obj.EncryptedMetadata,
|
2020-11-16 16:46:47 +00:00
|
|
|
&obj.EncryptedMetadataEncryptedKey,
|
2020-10-28 15:28:06 +00:00
|
|
|
|
2020-11-24 12:29:16 +00:00
|
|
|
&obj.TotalPlainSize,
|
2020-10-28 15:28:06 +00:00
|
|
|
&obj.TotalEncryptedSize,
|
|
|
|
&obj.FixedSegmentSize,
|
|
|
|
|
|
|
|
encryptionParameters{&obj.Encryption},
|
|
|
|
&obj.ZombieDeletionDeadline,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.New("testingGetAllObjects scan failed: %w", err)
|
|
|
|
}
|
|
|
|
objs = append(objs, obj)
|
|
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
|
|
return nil, Error.New("testingGetAllObjects scan failed: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(objs) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return objs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// testingGetAllSegments returns the state of the database.
|
|
|
|
func (db *DB) testingGetAllSegments(ctx context.Context) (_ []RawSegment, err error) {
|
|
|
|
segs := []RawSegment{}
|
|
|
|
|
2021-07-28 14:44:22 +01:00
|
|
|
rows, err := db.db.QueryContext(ctx, `
|
2023-07-24 16:45:32 +01:00
|
|
|
WITH ignore_full_scan_for_test AS (SELECT 1)
|
2020-10-29 18:10:46 +00:00
|
|
|
SELECT
|
2020-10-28 15:28:06 +00:00
|
|
|
stream_id, position,
|
2021-06-05 18:07:20 +01:00
|
|
|
created_at, repaired_at, expires_at,
|
2020-10-28 15:28:06 +00:00
|
|
|
root_piece_id, encrypted_key_nonce, encrypted_key,
|
|
|
|
encrypted_size,
|
|
|
|
plain_offset, plain_size,
|
2021-03-25 07:53:10 +00:00
|
|
|
encrypted_etag,
|
2020-10-28 15:28:06 +00:00
|
|
|
redundancy,
|
2022-01-27 00:01:03 +00:00
|
|
|
inline_data, remote_alias_pieces,
|
2021-10-27 09:50:27 +01:00
|
|
|
placement
|
2020-10-28 15:28:06 +00:00
|
|
|
FROM segments
|
2020-10-29 18:10:46 +00:00
|
|
|
ORDER BY stream_id ASC, position ASC
|
2020-10-28 15:28:06 +00:00
|
|
|
`)
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.New("testingGetAllSegments query: %w", err)
|
|
|
|
}
|
|
|
|
defer func() { err = errs.Combine(err, rows.Close()) }()
|
|
|
|
for rows.Next() {
|
|
|
|
var seg RawSegment
|
2021-02-08 09:33:45 +00:00
|
|
|
var aliasPieces AliasPieces
|
2020-10-28 15:28:06 +00:00
|
|
|
err := rows.Scan(
|
|
|
|
&seg.StreamID,
|
|
|
|
&seg.Position,
|
|
|
|
|
2021-03-12 17:43:30 +00:00
|
|
|
&seg.CreatedAt,
|
2021-03-17 09:52:26 +00:00
|
|
|
&seg.RepairedAt,
|
2021-06-05 18:07:20 +01:00
|
|
|
&seg.ExpiresAt,
|
2021-03-12 17:43:30 +00:00
|
|
|
|
2020-10-28 15:28:06 +00:00
|
|
|
&seg.RootPieceID,
|
|
|
|
&seg.EncryptedKeyNonce,
|
|
|
|
&seg.EncryptedKey,
|
|
|
|
|
|
|
|
&seg.EncryptedSize,
|
|
|
|
&seg.PlainOffset,
|
|
|
|
&seg.PlainSize,
|
2021-03-25 07:53:10 +00:00
|
|
|
&seg.EncryptedETag,
|
2020-10-28 15:28:06 +00:00
|
|
|
|
|
|
|
redundancyScheme{&seg.Redundancy},
|
|
|
|
|
|
|
|
&seg.InlineData,
|
2021-02-08 09:33:45 +00:00
|
|
|
&aliasPieces,
|
2021-10-27 09:50:27 +01:00
|
|
|
&seg.Placement,
|
2020-10-28 15:28:06 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.New("testingGetAllSegments scan failed: %w", err)
|
|
|
|
}
|
2021-02-08 09:33:45 +00:00
|
|
|
|
|
|
|
seg.Pieces, err = db.aliasCache.ConvertAliasesToPieces(ctx, aliasPieces)
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.New("testingGetAllSegments convert aliases to pieces failed: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-10-28 15:28:06 +00:00
|
|
|
segs = append(segs, seg)
|
|
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
|
|
return nil, Error.New("testingGetAllSegments scan failed: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(segs) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return segs, nil
|
|
|
|
}
|