satellite/metabase: add unique unversioned constraint for tests
While the index shouldn't be necessary as long as our implementation is correct, it still provides some additional checks for mistakes in the implementation. Change-Id: I7ed71ac99a979e375d7f94c8898e6f83ac623cb6
This commit is contained in:
parent
45fdc64300
commit
b2d2a8a744
@ -36,6 +36,8 @@ type Config struct {
|
|||||||
// TODO remove this flag when server-side copy implementation will be finished
|
// TODO remove this flag when server-side copy implementation will be finished
|
||||||
ServerSideCopy bool
|
ServerSideCopy bool
|
||||||
ServerSideCopyDisabled bool
|
ServerSideCopyDisabled bool
|
||||||
|
|
||||||
|
TestingUniqueUnversioned bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// DB implements a database for storing objects and segments.
|
// DB implements a database for storing objects and segments.
|
||||||
@ -306,18 +308,18 @@ func (db *DB) TestMigrateToLatest(ctx context.Context) error {
|
|||||||
bucket_name BYTEA NOT NULL,
|
bucket_name BYTEA NOT NULL,
|
||||||
object_key BYTEA NOT NULL,
|
object_key BYTEA NOT NULL,
|
||||||
stream_id BYTEA NOT NULL,
|
stream_id BYTEA NOT NULL,
|
||||||
|
|
||||||
created_at TIMESTAMPTZ NOT NULL default now(),
|
created_at TIMESTAMPTZ NOT NULL default now(),
|
||||||
expires_at TIMESTAMPTZ,
|
expires_at TIMESTAMPTZ,
|
||||||
|
|
||||||
encrypted_metadata_nonce BYTEA default NULL,
|
encrypted_metadata_nonce BYTEA default NULL,
|
||||||
encrypted_metadata BYTEA default NULL,
|
encrypted_metadata BYTEA default NULL,
|
||||||
encrypted_metadata_encrypted_key BYTEA default NULL,
|
encrypted_metadata_encrypted_key BYTEA default NULL,
|
||||||
|
|
||||||
encryption INT8 NOT NULL default 0,
|
encryption INT8 NOT NULL default 0,
|
||||||
|
|
||||||
zombie_deletion_deadline TIMESTAMPTZ default now() + '1 day',
|
zombie_deletion_deadline TIMESTAMPTZ default now() + '1 day',
|
||||||
|
|
||||||
PRIMARY KEY (project_id, bucket_name, object_key, stream_id)
|
PRIMARY KEY (project_id, bucket_name, object_key, stream_id)
|
||||||
)`,
|
)`,
|
||||||
`
|
`
|
||||||
@ -341,6 +343,19 @@ func (db *DB) TestMigrateToLatest(ctx context.Context) error {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if db.config.TestingUniqueUnversioned {
|
||||||
|
// This is only part of testing, because we do not want to affect the production performance.
|
||||||
|
migration.Steps = append(migration.Steps, &migrate.Step{
|
||||||
|
DB: &db.db,
|
||||||
|
Description: "Constraint for ensuring our metabase correctness.",
|
||||||
|
Version: 18,
|
||||||
|
Action: migrate.SQL{
|
||||||
|
`CREATE UNIQUE INDEX objects_one_unversioned_per_location ON objects (project_id, bucket_name, object_key) WHERE status IN ` + statusesUnversioned + `;`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return migration.Run(ctx, db.log.Named("migrate"))
|
return migration.Run(ctx, db.log.Named("migrate"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,12 +4,15 @@
|
|||||||
package metabase_test
|
package metabase_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"storj.io/common/testcontext"
|
"storj.io/common/testcontext"
|
||||||
|
"storj.io/common/testrand"
|
||||||
|
"storj.io/private/dbutil/pgutil/pgerrcode"
|
||||||
"storj.io/storj/satellite/metabase"
|
"storj.io/storj/satellite/metabase"
|
||||||
"storj.io/storj/satellite/metabase/metabasetest"
|
"storj.io/storj/satellite/metabase/metabasetest"
|
||||||
)
|
)
|
||||||
@ -22,3 +25,44 @@ func TestNow(t *testing.T) {
|
|||||||
require.WithinDuration(t, sysnow, now, 5*time.Second)
|
require.WithinDuration(t, sysnow, now, 5*time.Second)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDisallowDoubleUnversioned(t *testing.T) {
|
||||||
|
metabasetest.Run(t, func(ctx *testcontext.Context, t *testing.T, db *metabase.DB) {
|
||||||
|
// This checks that TestingUniqueUnversioned=true indeed works as needed.
|
||||||
|
objStream := metabasetest.RandObjectStream()
|
||||||
|
obj := metabasetest.CreateObject(ctx, t, db, objStream, 0)
|
||||||
|
|
||||||
|
internaldb := db.UnderlyingTagSQL()
|
||||||
|
_, err := internaldb.Exec(ctx, `
|
||||||
|
INSERT INTO objects (
|
||||||
|
project_id, bucket_name, object_key, version, stream_id,
|
||||||
|
status
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5,
|
||||||
|
`+strconv.Itoa(int(metabase.CommittedUnversioned))+`
|
||||||
|
)
|
||||||
|
`, obj.ProjectID, []byte(obj.BucketName), obj.ObjectKey, obj.Version+1, testrand.UUID(),
|
||||||
|
)
|
||||||
|
require.True(t, pgerrcode.IsConstraintViolation(err))
|
||||||
|
require.ErrorContains(t, err, "objects_one_unversioned_per_location")
|
||||||
|
|
||||||
|
_, err = internaldb.Exec(ctx, `
|
||||||
|
INSERT INTO objects (
|
||||||
|
project_id, bucket_name, object_key, version, stream_id,
|
||||||
|
status
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5,
|
||||||
|
`+strconv.Itoa(int(metabase.DeleteMarkerUnversioned))+`
|
||||||
|
)
|
||||||
|
`, obj.ProjectID, []byte(obj.BucketName), obj.ObjectKey, obj.Version+1, testrand.UUID(),
|
||||||
|
)
|
||||||
|
require.True(t, pgerrcode.IsConstraintViolation(err))
|
||||||
|
require.ErrorContains(t, err, "objects_one_unversioned_per_location")
|
||||||
|
|
||||||
|
metabasetest.Verify{
|
||||||
|
Objects: []metabase.RawObject{
|
||||||
|
metabase.RawObject(obj),
|
||||||
|
},
|
||||||
|
}.Check(ctx, t, db)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -84,11 +84,14 @@ func Run(t *testing.T, fn func(ctx *testcontext.Context, t *testing.T, db *metab
|
|||||||
)
|
)
|
||||||
|
|
||||||
RunWithConfig(t, metabase.Config{
|
RunWithConfig(t, metabase.Config{
|
||||||
ApplicationName: "satellite-metabase-test",
|
ApplicationName: "satellite-metabase-test",
|
||||||
MinPartSize: config.MinPartSize,
|
MinPartSize: config.MinPartSize,
|
||||||
MaxNumberOfParts: config.MaxNumberOfParts,
|
MaxNumberOfParts: config.MaxNumberOfParts,
|
||||||
|
|
||||||
ServerSideCopy: config.ServerSideCopy,
|
ServerSideCopy: config.ServerSideCopy,
|
||||||
ServerSideCopyDisabled: config.ServerSideCopyDisabled,
|
ServerSideCopyDisabled: config.ServerSideCopyDisabled,
|
||||||
|
|
||||||
|
TestingUniqueUnversioned: true,
|
||||||
}, fn)
|
}, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user