Skip to content

Commit

Permalink
Merge #139104
Browse files Browse the repository at this point in the history
139104: metamorphic: use globalSeed from `randutil.NewTestRand` r=stevendanna,RaduBerinde,herkolategan a=srosenberg

A minor quality of life improvement to ensure
metamorphic constants are derived from the
globalSeed. This makes it possible to reproduce
both the metamoprhic constants and the individual
test's rng values, from `COCKROACH_RANDOM_SEED`.

We also log the seed, when metamoprhic constants
are enabled. As of this change, we get a message
of this form, logged to `stderr`,
```
metamorphic: use COCKROACH_RANDOM_SEED=9076506593373491438 for reproduction
```

Epic: none
Release note: None

Co-authored-by: Stan Rosenberg <[email protected]>
  • Loading branch information
craig[bot] and srosenberg committed Jan 16, 2025
2 parents 0e13eaa + 4e70581 commit 93180aa
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 1 deletion.
9 changes: 8 additions & 1 deletion pkg/util/metamorphic/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ var (
r *rand.Rand
syncutil.Mutex
}
// Exposed for testing.
rngSeed int64
)

// Returns true iff the current process is eligible to enable metamorphic
Expand Down Expand Up @@ -267,8 +269,13 @@ func valueFromOverride[T any](name string, parser func(string) (T, error)) (T, b
func init() {
if metamorphicEligible() {
if !disableMetamorphicTesting {
rng.r, _ = randutil.NewTestRand()
rng.r, rngSeed = randutil.NewPseudoRandWithGlobalSeed()
metamorphicutil.IsMetamorphicBuild = rng.r.Float64() < IsMetamorphicBuildProbability
if metamorphicutil.IsMetamorphicBuild {
logf("metamorphic: use COCKROACH_RANDOM_SEED=%d for reproduction\n", rngSeed)
} else {
logf("IsMetamorphicBuild==false\n")
}
}

if overrideList, ok := envutil.EnvString(MetamorphicOverridesEnvVar, 0); ok {
Expand Down
24 changes: 24 additions & 0 deletions pkg/util/metamorphic/constants_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
package metamorphic

import (
"math/rand"
"testing"

"github.com/cockroachdb/cockroach/pkg/util/randutil"
"github.com/stretchr/testify/require"
)

Expand All @@ -27,3 +29,25 @@ func TestMetamorphicFromOverride(t *testing.T) {
)
require.Equal(t, 7, v)
}

// TestMetamorphicRngSeed checks that `constants.rngSeed` corresponds to `randutil.globalSeed`
func TestMetamorphicRngSeed(t *testing.T) {
// Make sure metamorphic testing is enabled.
require.True(t, metamorphicEligible())
require.False(t, disableMetamorphicTesting)
// N.B. We can't access `randutil.globalSeed` directly.
// Instead, the call below forces NewTestRand to reseed its iternal RNG with the globalSeed.
// The returned seed is _not_ the globalSeed, but one (application of) `Int63` away.
testRng, testSeed := randutil.NewTestRand()
// Thus, if rngSeed == globalSeed, it implies r(rngSeed).Int63() == testSeed.
// N.B. The converse doesn't necessarily hold, as two different seeds could yield the same PRNG sequence. However,
// it's extremely improbable. Hence, this assertion should suffice.
expectedSeed := rand.New(rand.NewSource(rngSeed)).Int63()
require.Equal(t, expectedSeed, testSeed)
// On the off-chance there is a collision, let's do 100 iterations. The probability of two different seeds yielding
// the same sequence of length 101 is infinitesimally small.
r := rand.New(rand.NewSource(expectedSeed))
for i := 0; i < 100; i++ {
require.Equal(t, r.Int63(), testRng.Int63())
}
}
10 changes: 10 additions & 0 deletions pkg/util/randutil/rand.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ func NewLockedPseudoRand() (*rand.Rand, int64) {
return rand.New(NewLockedSource(seed)), seed
}

// NewPseudoRandWithGlobalSeed returns an instance of math/rand.Rand, which is
// seeded with the global seed.
// It's _not_ intended to be called directly from a test; use NewTestRand for that.
// Instead, this function is useful for seeding other random number generators, on which the tests
// may depend; e.g., metamorphic constants.
// N.B. unlike NewTestRand, this function _never_ reseeds rng.
func NewPseudoRandWithGlobalSeed() (*rand.Rand, int64) {
return rand.New(rand.NewSource(globalSeed)), globalSeed
}

// NewTestRand returns an instance of math/rand.Rand seeded from rng, which is
// seeded with the global seed. If the caller is a test with a different
// path-qualified name than the previous caller, rng is reseeded from the global
Expand Down

0 comments on commit 93180aa

Please sign in to comment.