Skip to content

Commit 93c7263

Browse files
committed
feat(indexworker): add max users threshold for rollout
1 parent 33b87ae commit 93c7263

File tree

4 files changed

+79
-14
lines changed

4 files changed

+79
-14
lines changed

hack/test.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ GOTRUE_SAML_ENABLED="true"
132132
GOTRUE_SAML_PRIVATE_KEY="MIIEowIBAAKCAQEAszrVveMQcSsa0Y+zN1ZFb19cRS0jn4UgIHTprW2tVBmO2PABzjY3XFCfx6vPirMAPWBYpsKmXrvm1tr0A6DZYmA8YmJd937VUQ67fa6DMyppBYTjNgGEkEhmKuszvF3MARsIKCGtZqUrmS7UG4404wYxVppnr2EYm3RGtHlkYsXu20MBqSDXP47bQP+PkJqC3BuNGk3xt5UHl2FSFpTHelkI6lBynw16B+lUT1F96SERNDaMqi/TRsZdGe5mB/29ngC/QBMpEbRBLNRir5iUevKS7Pn4aph9Qjaxx/97siktK210FJT23KjHpgcUfjoQ6BgPBTLtEeQdRyDuc/CgfwIDAQABAoIBAGYDWOEpupQPSsZ4mjMnAYJwrp4ZISuMpEqVAORbhspVeb70bLKonT4IDcmiexCg7cQBcLQKGpPVM4CbQ0RFazXZPMVq470ZDeWDEyhoCfk3bGtdxc1Zc9CDxNMs6FeQs6r1beEZug6weG5J/yRn/qYxQife3qEuDMl+lzfl2EN3HYVOSnBmdt50dxRuX26iW3nqqbMRqYn9OHuJ1LvRRfYeyVKqgC5vgt/6Tf7DAJwGe0dD7q08byHV8DBZ0pnMVU0bYpf1GTgMibgjnLjK//EVWafFHtN+RXcjzGmyJrk3+7ZyPUpzpDjO21kpzUQLrpEkkBRnmg6bwHnSrBr8avECgYEA3pq1PTCAOuLQoIm1CWR9/dhkbJQiKTJevlWV8slXQLR50P0WvI2RdFuSxlWmA4xZej8s4e7iD3MYye6SBsQHygOVGc4efvvEZV8/XTlDdyj7iLVGhnEmu2r7AFKzy8cOvXx0QcLg+zNd7vxZv/8D3Qj9Jje2LjLHKM5n/dZ3RzUCgYEAzh5Lo2anc4WN8faLGt7rPkGQF+7/18ImQE11joHWa3LzAEy7FbeOGpE/vhOv5umq5M/KlWFIRahMEQv4RusieHWI19ZLIP+JwQFxWxS+cPp3xOiGcquSAZnlyVSxZ//dlVgaZq2o2MfrxECcovRlaknl2csyf+HjFFwKlNxHm2MCgYAr//R3BdEy0oZeVRndo2lr9YvUEmu2LOihQpWDCd0fQw0ZDA2kc28eysL2RROte95r1XTvq6IvX5a0w11FzRWlDpQ4J4/LlcQ6LVt+98SoFwew+/PWuyLmxLycUbyMOOpm9eSc4wJJZNvaUzMCSkvfMtmm5jgyZYMMQ9A2Ul/9SQKBgB9mfh9mhBwVPIqgBJETZMMXOdxrjI5SBYHGSyJqpT+5Q0vIZLfqPrvNZOiQFzwWXPJ+tV4Mc/YorW3rZOdo6tdvEGnRO6DLTTEaByrY/io3/gcBZXoSqSuVRmxleqFdWWRnB56c1hwwWLqNHU+1671FhL6pNghFYVK4suP6qu4BAoGBAMk+VipXcIlD67mfGrET/xDqiWWBZtgTzTMjTpODhDY1GZck1eb4CQMP5j5V3gFJ4cSgWDJvnWg8rcz0unz/q4aeMGl1rah5WNDWj1QKWMS6vJhMHM/rqN1WHWR0ZnV83svYgtg0zDnQKlLujqW4JmGXLMU7ur6a+e6lpa1fvLsP"
133133
GOTRUE_MAX_VERIFIED_FACTORS=10
134134
GOTRUE_SMS_TEST_OTP_VALID_UNTIL=""
135+
GOTRUE_INDEX_WORKER_MAX_USERS_THRESHOLD=1000000
135136
GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPT=true
136137
GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY_ID=abc
137138
GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY=pwFoiPyybQMqNmYVN0gUnpbfpGQV2sDv9vp0ZAxi_Y4

internal/conf/configuration.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1359,5 +1359,6 @@ func (t *SmsProviderConfiguration) IsTwilioVerifyProvider() bool {
13591359

13601360
// IndexWorkerConfiguration holds the configuration for database indexes.
13611361
type IndexWorkerConfiguration struct {
1362-
EnsureUserSearchIndexesExist bool `json:"ensure_user_search_indexes_exist" split_words:"true" default:"false"`
1362+
EnsureUserSearchIndexesExist bool `json:"ensure_user_search_indexes_exist" split_words:"true" default:"false"`
1363+
MaxUsersThreshold int64 `json:"max_users_threshold" split_words:"true" default:"0"`
13631364
}

internal/indexworker/indexworker.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,24 @@ func CreateIndexes(ctx context.Context, config *conf.GlobalConfiguration, le *lo
147147
}
148148
}
149149

150-
userCount, err := getApproximateUserCount(db, config.DB.Namespace)
151-
if err != nil {
152-
le.WithError(err).Warn("Failed to get approximate user count, proceeding with index creation")
150+
if config.IndexWorker.MaxUsersThreshold > 0 {
151+
userCount, err := getApproximateUserCount(db, config.DB.Namespace)
152+
if err != nil {
153+
le.WithError(err).Warn("Failed to get approximate user count, proceeding with index creation")
154+
}
155+
if userCount > config.IndexWorker.MaxUsersThreshold {
156+
le.WithFields(logrus.Fields{
157+
"code": "index_creation_skipped",
158+
"user_count": userCount,
159+
"max_users_threshold": config.IndexWorker.MaxUsersThreshold,
160+
}).Info("Skipping index creation because user count exceeds threshold")
161+
return nil
162+
}
163+
le.WithFields(logrus.Fields{
164+
"code": "index_creation_starting",
165+
"user_count": userCount,
166+
}).Info("Starting index creation")
153167
}
154-
le.WithFields(logrus.Fields{
155-
"code": "index_creation_starting",
156-
"user_count": userCount,
157-
}).Info("Starting index creation")
158168

159169
// First, clean up any invalid indexes from previous interrupted attempts
160170
dropInvalidIndexes(db, le, config.DB.Namespace, indexNames)

internal/indexworker/indexworker_test.go

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,26 @@ import (
1515
"github.com/stretchr/testify/require"
1616
"github.com/stretchr/testify/suite"
1717
"github.com/supabase/auth/internal/conf"
18+
"github.com/supabase/auth/internal/models"
1819
"github.com/supabase/auth/internal/storage"
1920
)
2021

2122
type IndexWorkerTestSuite struct {
2223
suite.Suite
23-
config *conf.GlobalConfiguration
24-
db *storage.Connection
25-
popDB *pop.Connection
26-
namespace string
27-
logger *logrus.Entry
24+
config *conf.GlobalConfiguration
25+
db *storage.Connection
26+
popDB *pop.Connection
27+
namespace string
28+
logger *logrus.Entry
29+
maxUsersThreshold int64
2830
}
2931

3032
func (ts *IndexWorkerTestSuite) SetupSuite() {
3133
// Load test configuration
3234
config, err := conf.LoadGlobal("../../hack/test.env")
3335
require.NoError(ts.T(), err)
3436
ts.config = config
37+
ts.maxUsersThreshold = config.IndexWorker.MaxUsersThreshold
3538
ts.namespace = config.DB.Namespace
3639
ts.logger = logrus.NewEntry(logrus.New())
3740
ts.logger.Logger.SetLevel(logrus.DebugLevel)
@@ -69,8 +72,9 @@ func (ts *IndexWorkerTestSuite) TearDownSuite() {
6972
}
7073

7174
func (ts *IndexWorkerTestSuite) SetupTest() {
72-
// Clean up before each test
75+
models.TruncateAll(ts.db)
7376
ts.cleanupIndexes()
77+
ts.config.IndexWorker.MaxUsersThreshold = ts.maxUsersThreshold
7478
}
7579

7680
func (ts *IndexWorkerTestSuite) cleanupIndexes() {
@@ -282,6 +286,55 @@ func getIndexNames(indexes []struct {
282286
return names
283287
}
284288

289+
func (ts *IndexWorkerTestSuite) TestMaxUsersThresholdSkipsIndexCreation() {
290+
ctx := context.Background()
291+
292+
// SetupTest already called TruncateAll, so the users table is empty.
293+
// Insert test users so the approximate count exceeds the threshold.
294+
for i := 0; i < 5; i++ {
295+
insertQuery := fmt.Sprintf(
296+
`INSERT INTO %q.users (instance_id, id, aud, role, email, encrypted_password, created_at, updated_at) VALUES ('00000000-0000-0000-0000-000000000000', gen_random_uuid(), 'authenticated', 'authenticated', 'threshold_test_%d@example.com', '', now(), now())`,
297+
ts.namespace, i,
298+
)
299+
require.NoError(ts.T(), ts.db.RawQuery(insertQuery).Exec())
300+
}
301+
// Update pg_class.reltuples so getApproximateUserCount reflects the inserts
302+
analyzeQuery := fmt.Sprintf(`ANALYZE %q.users`, ts.namespace)
303+
require.NoError(ts.T(), ts.db.RawQuery(analyzeQuery).Exec())
304+
305+
ts.config.IndexWorker.MaxUsersThreshold = 1
306+
307+
indexes := getUsersIndexes(ts.namespace)
308+
existingIndexes, err := getIndexStatuses(ts.popDB, ts.namespace, getIndexNames(indexes))
309+
require.NoError(ts.T(), err)
310+
assert.Empty(ts.T(), existingIndexes, "No indexes should exist before the test")
311+
312+
err = CreateIndexes(ctx, ts.config, ts.logger)
313+
require.NoError(ts.T(), err)
314+
315+
existingIndexes, err = getIndexStatuses(ts.popDB, ts.namespace, getIndexNames(indexes))
316+
require.NoError(ts.T(), err)
317+
assert.Empty(ts.T(), existingIndexes, "No indexes should be created when user count exceeds threshold")
318+
}
319+
320+
func (ts *IndexWorkerTestSuite) TestMaxUsersThresholdZeroCreatesIndexes() {
321+
ctx := context.Background()
322+
323+
ts.config.IndexWorker.MaxUsersThreshold = 0
324+
325+
err := CreateIndexes(ctx, ts.config, ts.logger)
326+
require.NoError(ts.T(), err)
327+
328+
indexes := getUsersIndexes(ts.namespace)
329+
existingIndexes, err := getIndexStatuses(ts.popDB, ts.namespace, getIndexNames(indexes))
330+
require.NoError(ts.T(), err)
331+
assert.Equal(ts.T(), len(indexes), len(existingIndexes), "All indexes should be created when threshold is 0 (no limit)")
332+
for _, idx := range existingIndexes {
333+
assert.True(ts.T(), idx.IsValid, "Index %s should be valid", idx.IndexName)
334+
assert.True(ts.T(), idx.IsReady, "Index %s should be ready", idx.IndexName)
335+
}
336+
}
337+
285338
// TestCreateIndexesWithInvalidIndexes tests that CreateIndexes can recover from invalid indexes
286339
// This test simulates a scenario where indexes become invalid (e.g., from interrupted CONCURRENT creation)
287340
// and verifies that CreateIndexes properly handles them by dropping and recreating.

0 commit comments

Comments
 (0)