Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions drivers/db2/internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ func (c *Config) Validate() error {
c.MaxThreads = constants.DefaultThreadCount
}

if c.RetryCount < 0 {
return fmt.Errorf("retry count is required")
}
if c.RetryCount == 0 {
c.RetryCount = constants.DefaultRetryCount
Comment on lines +91 to +95

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a space between two if conditions and do it for all the drivers wherever needed?

}

if c.SSLConfiguration == nil {
c.SSLConfiguration = &utils.SSLConfig{
Mode: "disable",
Expand Down
13 changes: 13 additions & 0 deletions drivers/db2/internal/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ func TestConfig_Validate(t *testing.T) {
},
expectErr: true,
},
{
// Rejects config with a negative retry count.
name: "invalid config - negative retry count",
config: &Config{
Host: "db2-host",
Port: 50000,
Database: "testdb",
Username: "db2inst1",
Password: "secret1234",
RetryCount: -1,
},
expectErr: true,
},
}

for _, tt := range tests {
Expand Down
2 changes: 0 additions & 2 deletions drivers/db2/internal/db2.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/datazip-inc/olake/drivers/abstract"
"github.com/datazip-inc/olake/pkg/jdbc"
"github.com/datazip-inc/olake/types"
"github.com/datazip-inc/olake/utils"
"github.com/datazip-inc/olake/utils/logger"
"github.com/jmoiron/sqlx"
"golang.org/x/crypto/ssh"
Expand Down Expand Up @@ -79,7 +78,6 @@ func (d *DB2) Setup(ctx context.Context) error {
}

d.client = client
d.config.RetryCount = utils.Ternary(d.config.RetryCount <= 0, 1, d.config.RetryCount+1).(int)
return nil
Comment on lines -82 to 81

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In olake , by backoff_retry_count we mean is number of retry count for example if user enters 3 backoff retry count than it means 1 main sync + 3 Retry count so can you please make changes according to that.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeahh that makes sense, i have updated all drivers to align with this semantic ( attempts = RetryCount + 1 )

please lmk if this looks good

}

Expand Down
5 changes: 4 additions & 1 deletion drivers/kafka/internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ func (c *Config) Validate() error {
c.MaxThreads = constants.DefaultThreadCount
}

if c.RetryCount <= 0 {
if c.RetryCount < 0 {
return fmt.Errorf("retry count is required")
}
if c.RetryCount == 0 {
c.RetryCount = constants.DefaultRetryCount
}

Expand Down
4 changes: 0 additions & 4 deletions drivers/kafka/internal/kafka.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,6 @@ func (k *Kafka) Setup(ctx context.Context) error {
logger.Infof("initialized schema registry client for endpoint: %s", k.config.SchemaRegistry.Endpoint)
}

// TODO: Avoid modifying the configured/default retry count during initialization across all drivers.
// check for default backoff count
k.config.RetryCount = utils.Ternary(k.config.RetryCount <= 0, 1, k.config.RetryCount+1).(int)

return nil
}

Expand Down
2 changes: 1 addition & 1 deletion drivers/mongodb/internal/backfill.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (m *Mongo) GetOrSplitChunks(ctx context.Context, pool *destination.WriterPo
// Generate and update chunks
var retryErr error
var chunksArray []types.Chunk
err = utils.RetryOnBackoff(ctx, m.config.RetryCount, constants.DefaultRetryTimeout, func(ctx context.Context) error {
err = utils.RetryOnBackoff(ctx, m.MaxRetries(), constants.DefaultRetryTimeout, func(ctx context.Context) error {
chunksArray, retryErr = m.splitChunks(ctx, collection, stream, storageSize)
return retryErr
})
Expand Down
5 changes: 4 additions & 1 deletion drivers/mongodb/internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ func (c *Config) Validate() error {
c.MaxThreads = constants.DefaultThreadCount
}

if c.RetryCount <= 0 {
if c.RetryCount < 0 {
return fmt.Errorf("retry count is required")
}
if c.RetryCount == 0 {
c.RetryCount = constants.DefaultRetryCount
}

Expand Down
12 changes: 12 additions & 0 deletions drivers/mongodb/internal/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,18 @@ func TestConfig_Validate(t *testing.T) {
},
expectErr: true,
},
{
name: "negative retry count fails validation",
config: &Config{
Hosts: []string{"mongo.example.com:27017"},
Database: "testdb",
Username: "user",
Password: "pass",
AuthDB: "admin",
RetryCount: -1,
},
expectErr: true,
},
{
name: "sets defaults",
config: &Config{
Expand Down
2 changes: 0 additions & 2 deletions drivers/mongodb/internal/mon.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,6 @@ func (m *Mongo) Setup(ctx context.Context) error {
m.client = conn
// no need to check from discover if it have cdc support or not
m.CDCSupport = true
// check for default backoff count
m.config.RetryCount = utils.Ternary(m.config.RetryCount == 0, 1, m.config.RetryCount+1).(int)
pingCtx, cancel := context.WithTimeout(ctx, 1*time.Minute)
Comment on lines 173 to 174

@saksham-datazip saksham-datazip Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what i was suggesting is instead of removing this part you can let it stay as it is and just remove that check from validate function so it would be single source of truth and just add a small comment over here specifying why we are doing +1 .
if you have some other reasoning for the changes you made than feel free to discuss.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, i actually agree the +1 is intentional, my pr doesnt remove it, it relocates it, retry_count is the no. of retries in RetryOnBackoff expects total attempts, so Maxretries now returns retrycount + 1 ( same behaviour btw ) but the conversation happens at the interface boundary instead of mutating config,

and on single source of truth: the line was copy-pasted into 9 drivers setup() method. after my change it exists once i.e constants. DefaultRetryCount + Validate().

also, keeping it in Setup() cant fix the actual bug cleanly imo, like the unset retry_count became 1 attempt (zero retries). That fix requires an inline DefaultRetryCount+1 inside the Ternary, which is exactly what needs the comment you're asking for. In Validate() it reads naturally, and it follows the existing MaxThreads precedent in the same file

i think the middle ground would be : i'll add the comment you suggested at the conversion point in MaxRetries() , like

// RetryCount is the number of retries; +1 accounts for the initial attempt,
// since RetryOnBackoff expects total attempts.
return p.config.RetryCount + 1

lmk if that works

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand what you mean. I think we can handle this in the Setup() function itself: if RetryCount < 0, we fail the sync, and if RetryCount == 0, we set it to the default retry count.

if c.RetryCount < 0 {
    return fmt.Errorf("retry count is required")
}

if c.RetryCount == 0 {
    c.RetryCount = constants.DefaultRetryCount
}

Apart from that, instead of modifying the value in MaxRetries, we can handle the +1 in a single place inside RetryOnBackoff() and add a comment explaining why it is needed:

func RetryOnBackoff(ctx context.Context, attempts int, sleep time.Duration, f func(ctx context.Context) error) (err error) {
    // Add 1 because attempts represents the number of retries,
    // while the function needs the total number of attempts.
    attempts = attempts + 1

This way, we are not manipulating the input provided by the user in multiple places, and the retry-count logic stays centralized in RetryOnBackoff().

And just FYI, in your approach, Mongo is directly calling RetryOnBackoff like this:

err = utils.RetryOnBackoff(ctx, m.config.RetryCount, constants.DefaultRetryTimeout, func(ctx context.Context) error {
    chunksArray, retryErr = m.splitChunks(ctx, collection, stream, storageSize)
    return retryErr
})

So it would bypass the changes you're making in MaxRetries and use default values.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also their is one more thing we have changed the scope of this pr that we dont want config file to be manipulated directly so can you once check in entire pr that what we should do for instance in mysql if db name is empty we are setting it to "mysql" but we should fail the sync itself can you once check and made the respective changes

defer cancel()

Expand Down
5 changes: 4 additions & 1 deletion drivers/mssql/internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ func (c *Config) Validate() error {
c.MaxThreads = constants.DefaultThreadCount
}

if c.RetryCount <= 0 {
if c.RetryCount < 0 {
return fmt.Errorf("retry count is required")
}
if c.RetryCount == 0 {
c.RetryCount = constants.DefaultRetryCount
}

Expand Down
13 changes: 13 additions & 0 deletions drivers/mssql/internal/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,19 @@ func TestConfig_Validate(t *testing.T) {
},
expectErr: true,
},
{
// Rejects config with a negative retry count.
name: "invalid config - negative retry count",
config: &Config{
Host: "mssql-host",
Port: 1433,
Database: "testdb",
Username: "sa",
Password: "Password!123",
RetryCount: -1,
},
expectErr: true,
},
{
name: "valid config with primary_config",
config: &Config{
Expand Down
1 change: 0 additions & 1 deletion drivers/mssql/internal/mssql.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ func (m *MSSQL) Setup(ctx context.Context) error {
return fmt.Errorf("failed to connect to MSSQL: %s", err)
}

m.config.RetryCount = utils.Ternary(m.config.RetryCount <= 0, 1, m.config.RetryCount+1).(int)
// Enable CDC support if database-level CDC is enabled
cdcSupported, err := m.isDatabaseCDCEnabled(ctx)
if err != nil {
Expand Down
8 changes: 5 additions & 3 deletions drivers/mysql/internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,8 @@ func (c *Config) Validate() error {
return fmt.Errorf("password is required")
}

// Optional database name, default to 'mysql'
if c.Database == "" {
c.Database = "mysql"
return fmt.Errorf("database name is required")
}
Comment on lines 111 to 113

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey sorry for the to and fro, we had an internal discussion and after thinking this particular change might be destructive and of low priority so can you please revert it and dont forget to revert the respective unit test for this fail ?


// Set default number of threads if not provided
Expand All @@ -118,7 +117,10 @@ func (c *Config) Validate() error {
}

// Set default retry count if not provided
if c.RetryCount <= 0 {
if c.RetryCount < 0 {
return fmt.Errorf("retry count is required")
}
if c.RetryCount == 0 {
c.RetryCount = constants.DefaultRetryCount // Reasonable default for retries
}

Expand Down
22 changes: 16 additions & 6 deletions drivers/mysql/internal/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,17 +325,30 @@ func TestConfig_Validate(t *testing.T) {
},
expectErr: true,
},
// Defaults database to mysql when empty.
// Rejects empty database.
{
name: "valid config - defaults database",
name: "invalid config - missing database",
config: &Config{
Host: "localhost",
Port: 3306,
Username: "testuser",
Password: "testpass",
Database: "",
},
expectErr: false,
expectErr: true,
},
// Rejects negative retry count.
{
name: "invalid config - negative retry count",
config: &Config{
Host: "localhost",
Port: 3306,
Username: "testuser",
Password: "testpass",
Database: "testdb",
RetryCount: -1,
},
expectErr: true,
},
}

Expand All @@ -348,9 +361,6 @@ func TestConfig_Validate(t *testing.T) {
if !tt.expectErr && err != nil {
t.Errorf("Expected no error but got: %v", err)
}
if !tt.expectErr && err == nil && tt.config.Database == "" {
t.Errorf("Expected database to default to 'mysql'")
}
})
}
}
Expand Down
1 change: 0 additions & 1 deletion drivers/mysql/internal/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ func (m *MySQL) Setup(ctx context.Context) error {
m.cdcConfig = *cdc
}
m.client = client
m.config.RetryCount = utils.Ternary(m.config.RetryCount <= 0, 1, m.config.RetryCount+1).(int)
// Enable CDC support if binlog is configured
cdcSupported, err := m.IsCDCSupported(ctx)
if err != nil {
Expand Down
7 changes: 7 additions & 0 deletions drivers/oracle/internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ func (c *Config) Validate() error {
c.MaxThreads = constants.DefaultThreadCount
}

if c.RetryCount < 0 {
return fmt.Errorf("retry count is required")
}
if c.RetryCount == 0 {
c.RetryCount = constants.DefaultRetryCount
}

if c.SSLConfiguration == nil {
c.SSLConfiguration = &utils.SSLConfig{
Mode: "disable",
Expand Down
1 change: 0 additions & 1 deletion drivers/oracle/internal/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ func (o *Oracle) Setup(ctx context.Context) error {
}

o.client = client
o.config.RetryCount = utils.Ternary(o.config.RetryCount <= 0, 1, o.config.RetryCount+1).(int)
return nil
}

Expand Down
12 changes: 12 additions & 0 deletions drivers/postgres/internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,23 @@ func (c *Config) Validate() error {
return fmt.Errorf("invalid port number: must be between 1 and 65535")
}

if c.Database == "" {
return fmt.Errorf("database name is required")
}

// default number of threads
if c.MaxThreads <= 0 {
c.MaxThreads = constants.DefaultThreadCount
}

// default backoff retry count
if c.RetryCount < 0 {
return fmt.Errorf("retry count is required")
}
if c.RetryCount == 0 {
c.RetryCount = constants.DefaultRetryCount
}

// Add the connection parameters to the url
parsed := &url.URL{
Scheme: "postgres",
Expand Down
23 changes: 23 additions & 0 deletions drivers/postgres/internal/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,29 @@ func TestConfig_Validate(t *testing.T) {
},
expectErr: true,
},
{
name: "invalid missing database",
config: &Config{
Host: "localhost",
Port: 5432,
Username: "postgres",
Password: "secret",
Database: "",
},
expectErr: true,
},
{
name: "invalid negative retry count",
config: &Config{
Host: "localhost",
Port: 5432,
Username: "postgres",
Password: "secret",
Database: "postgres",
RetryCount: -1,
},
expectErr: true,
},
}

for _, tt := range tests {
Expand Down
1 change: 0 additions & 1 deletion drivers/postgres/internal/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ func (p *Postgres) Setup(ctx context.Context) error {
logger.Info("Standard Replication is selected")
}
p.client = pgClient
p.config.RetryCount = utils.Ternary(p.config.RetryCount <= 0, 1, p.config.RetryCount+1).(int)
return nil
}

Expand Down
5 changes: 4 additions & 1 deletion drivers/s3/internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ func (c *Config) Validate() error {
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also can you make the same changes for this one as well as i told you and also do this for other drivers as well


// Set default retry count
if c.RetryCount <= 0 {
if c.RetryCount < 0 {
return fmt.Errorf("retry count is required")
}
if c.RetryCount == 0 {
c.RetryCount = constants.DefaultRetryCount
}

Expand Down
3 changes: 3 additions & 0 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,9 @@ func RetryWithSkip(ctx context.Context, maxRetries int, sleep time.Duration, sho

// RetryOnBackoff retries the function f up to attempts times with a backoff sleep between attempts.
func RetryOnBackoff(ctx context.Context, attempts int, sleep time.Duration, f func(ctx context.Context) error) (err error) {
// Add 1 because attempts represents the number of retries,
// while the function needs the total number of attempts.
attempts = attempts + 1
for cur := range attempts {
select {
case <-ctx.Done():
Expand Down