Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for creating namespaced databases #294

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

neekolas
Copy link
Contributor

@neekolas neekolas commented Nov 26, 2024

TL;DR

Added support for namespaced databases with automatic creation and migration capabilities.

What changed?

  • Introduced NewNamespacedDB function to create and manage isolated database instances
  • Refactored database connection logic into smaller, focused functions
  • Added createNamespace function to handle database creation
  • Implemented waitUntilDBReady for connection readiness checks
  • Added unit tests to verify namespaced database functionality
  • Made test database DSN constants public for reuse

How to test?

  1. Run the new unit test TestNamespacedDB
  2. Verify that a new database is created with the specified namespace
  3. Confirm that the created database is properly initialized with migrations
  4. Check that the database name matches the provided namespace

Why make this change?

This change enables better isolation between different database instances, allowing for separate namespaces per instance. This is particularly useful for testing scenarios and multi-tenant applications where data separation is crucial.

Ticket

#225

Summary by CodeRabbit

  • New Features
    • Introduced a function to check database readiness and a function to create a namespaced database.
    • Enhanced database configuration handling with a new parsing function.
  • Bug Fixes
    • Improved error handling during namespace creation.
  • Tests
    • Added tests to validate the creation and querying of namespaced databases, including support for multiple instances with the same name and error handling for invalid inputs.
  • Documentation
    • Updated visibility of constants for better accessibility in the test utilities.

Copy link

coderabbitai bot commented Nov 26, 2024

Walkthrough

The changes in this pull request involve significant modifications to the pkg/db/pgx.go file, including the introduction of several new functions for database management and configuration. The newPGXDB function has been updated to accept a pgxpool.Config object instead of a DSN string. New functions include waitUntilDBReady, parseConfig, createNamespace, and NewNamespacedDB, which collectively enhance the modularity and error handling of the database connection process. Additionally, several new test functions have been added to validate the creation of namespaced databases, and two constants in pkg/testutils/store.go have been made exported.

Changes

File Change Summary
pkg/db/pgx.go - Updated newPGXDB to accept pgxpool.Config instead of DSN.
- Added waitUntilDBReady function.
- Added parseConfig function.
- Added createNamespace function.
- Added NewNamespacedDB function.
- Updated logic in NewDB function (signature unchanged).
pkg/db/pgx_test.go - Added TestNamespacedDB function to validate namespaced database creation.
- Added TestNamespaceRepeat function to validate multiple creations of the same namespaced database.
- Added TestNamespacedDBInvalidName and TestNamespacedDBInvalidDSN for error handling.
pkg/testutils/store.go - Changed visibility of constants localTestDBDSNPrefix and localTestDBDSNSuffix to LocalTestDBDSNPrefix and LocalTestDBDSNSuffix.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant DBManager
    participant DB

    Client->>DBManager: NewNamespacedDB(dsn, namespace)
    DBManager->>DBManager: parseConfig(dsn)
    DBManager->>DB: createNamespace(namespace)
    DB->>DBManager: Namespace created or exists
    DBManager->>DB: newPGXDB(config)
    DB->>DBManager: Database connection established
    DBManager->>Client: Return DB connection
Loading

Warning

Rate limit exceeded

@neekolas has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 6 minutes and 25 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 4e6c14c and f1897ed.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor Author

neekolas commented Nov 26, 2024

This stack of pull requests is managed by Graphite. Learn more about stacking.

@neekolas neekolas mentioned this pull request Nov 26, 2024
@neekolas neekolas marked this pull request as ready for review November 26, 2024 20:56
@neekolas neekolas requested a review from a team as a code owner November 26, 2024 20:56
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Outside diff range and nitpick comments (4)
pkg/db/pgx_test.go (1)

16-22: Consider increasing timeouts for CI environments.

The 1-second timeouts might be too aggressive for CI environments where database operations could be slower.

Consider:

  1. Making timeouts configurable via environment variables
  2. Using longer default timeouts (e.g., 5-10 seconds)
  3. Adding retry logic for transient failures
pkg/testutils/store.go (2)

16-17: Consider making database configuration more flexible and secure

While making these constants public supports the namespaced databases feature, exposing hardcoded credentials and connection details through exported constants raises some concerns:

  1. Hardcoded credentials in source code
  2. Fixed port number that might not match all test environments
  3. Disabled SSL mode in the connection string

Consider:

  1. Using environment variables for credentials and connection details
  2. Adding documentation about the expected test database configuration
  3. Making SSL mode configurable for different test environments

Line range hint 1-94: Consider enhancing test database management

The current implementation provides a solid foundation for test database management, but as we add support for namespaced databases, consider these architectural improvements:

  1. Add a configuration struct to centralize test database settings
  2. Implement connection pooling for test databases to improve performance
  3. Add parallel test support with database name prefixes to prevent conflicts
  4. Consider adding cleanup timeout handling for long-running tests

Would you like help implementing any of these improvements?

pkg/db/pgx.go (1)

15-25: Consider using context deadlines instead of custom timing logic

Currently, waitUntilDBReady manually calculates the timeout using time.Now().Add(waitTime). Using context deadlines can simplify the code and ensure better propagation of cancellation signals.

Apply this diff to refactor the function:

 func waitUntilDBReady(ctx context.Context, db *pgxpool.Pool, waitTime time.Duration) error {
-	waitUntil := time.Now().Add(waitTime)
+	ctx, cancel := context.WithTimeout(ctx, waitTime)
+	defer cancel()

-	err := db.Ping(ctx)
+	for {
+		err := db.Ping(ctx)
+		if err == nil {
+			return nil
+		}
+
+		select {
+		case <-time.After(3 * time.Second):
+			continue
+		case <-ctx.Done():
+			return ctx.Err()
+		}
+	}
-
-	for err != nil && time.Now().Before(waitUntil) {
-		time.Sleep(3 * time.Second)
-		err = db.Ping(ctx)
-	}
-	return err
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f05ea4c and 3671479.

📒 Files selected for processing (3)
  • pkg/db/pgx.go (2 hunks)
  • pkg/db/pgx_test.go (1 hunks)
  • pkg/testutils/store.go (2 hunks)
🔇 Additional comments (4)
pkg/db/pgx_test.go (1)

1-10: LGTM!

The package declaration and imports are appropriate for a database test file.

pkg/testutils/store.go (2)

31-31: LGTM!

The usage of the exported constants is correct and consistent.


39-39: Verify database name sanitization

While using random strings for database names helps prevent conflicts, we should ensure proper validation of the generated name to prevent potential SQL injection vulnerabilities.

Let's check the implementation of RandomStringLower:

Additionally, consider adding a validation regex for the database name before using it in the SQL query.

✅ Verification successful

Let me check the implementation of RandomString and analyze how the database name is used.


Let me check the definition of letterRunes and look for any database creation code.


Let me check one final thing to see the complete random string generation implementation.


Database name generation is secure against SQL injection

The implementation is safe because:

  1. The random database name is generated using only alphanumeric characters (a-z, A-Z) from a predefined set of letterRunes
  2. The database creation is properly quoted in both implementations:
    • CREATE DATABASE " + dbName in testutils/store.go
    • CREATE DATABASE "%s" with proper formatting in pgx.go
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find and analyze the implementation of RandomStringLower
ast-grep --pattern 'func RandomStringLower($$$) $_' 

Length of output: 324


Script:

#!/bin/bash
# Find the implementation of RandomString
ast-grep --pattern 'func RandomString($$$) $_'

# Check how the database name is used in openDB
ast-grep --pattern 'func openDB($$$) $_'

Length of output: 1114


Script:

#!/bin/bash
# Find the definition of letterRunes
ast-grep --pattern 'letterRunes := $_'
ast-grep --pattern 'letterRunes = $_'
ast-grep --pattern 'var letterRunes $_'

# Look for database creation code
rg -A 5 "CREATE DATABASE" 

Length of output: 2313


Script:

#!/bin/bash
# Find the complete file content to see letterRunes declaration
cat pkg/testutils/random.go

Length of output: 1239

pkg/db/pgx.go (1)

7-7: Unused import: "strings" package

The "strings" package is imported but may no longer be necessary after refactoring error handling.

Run the following script to check if "strings" is still used in the codebase:

If no usages are found, you can safely remove the import:

 import (
 	"context"
 	"database/sql"
 	"fmt"
-	"strings"
 	"time"

 	"github.com/jackc/pgx/v5/pgxpool"
 	"github.com/jackc/pgx/v5/stdlib"
 	"github.com/xmtp/xmtpd/pkg/migrations"
 )

pkg/db/pgx_test.go Outdated Show resolved Hide resolved
pkg/db/pgx_test.go Outdated Show resolved Hide resolved
pkg/db/pgx.go Outdated Show resolved Hide resolved
pkg/db/pgx.go Outdated Show resolved Hide resolved
@neekolas neekolas force-pushed the 11-26-add_support_for_creating_namespaced_databases branch 3 times, most recently from 21e80d5 to 6e15894 Compare November 27, 2024 00:36
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (2)
pkg/db/pgx_test.go (1)

12-35: Consider test improvements for robustness

A few suggestions to enhance the test:

  1. The hardcoded 1-second timeouts might be too aggressive for slower systems or high-load environments
  2. The test could benefit from parallel execution support

Consider these improvements:

 func TestNamespacedDB(t *testing.T) {
+    t.Parallel()
     startingDsn := testutils.LocalTestDBDSNPrefix + "/foo" + testutils.LocalTestDBDSNSuffix
     newDBName := testutils.RandomString(32)
     // Create namespaced DB
     namespacedDB, err := NewNamespacedDB(
         context.Background(),
         startingDsn,
         newDBName,
-        time.Second,
-        time.Second,
+        5*time.Second,  // Increased timeout for slower systems
+        5*time.Second,
     )
pkg/db/pgx.go (1)

15-25: Consider enhancing the retry mechanism

The current implementation could be improved in several ways:

  1. Use exponential backoff instead of a fixed 3-second interval
  2. Add logging for retry attempts
  3. Provide more context in the returned error
 func waitUntilDBReady(ctx context.Context, db *pgxpool.Pool, waitTime time.Duration) error {
 	waitUntil := time.Now().Add(waitTime)
+	retryInterval := time.Second
 	err := db.Ping(ctx)
 
 	for err != nil && time.Now().Before(waitUntil) {
-		time.Sleep(3 * time.Second)
+		log.Printf("Database not ready, retrying in %v: %v", retryInterval, err)
+		time.Sleep(retryInterval)
+		retryInterval = min(retryInterval*2, 10*time.Second)
 		err = db.Ping(ctx)
 	}
-	return err
+	if err != nil {
+		return fmt.Errorf("database not ready after %v: %w", waitTime, err)
+	}
+	return nil
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 3671479 and 6e15894.

📒 Files selected for processing (3)
  • pkg/db/pgx.go (2 hunks)
  • pkg/db/pgx_test.go (1 hunks)
  • pkg/testutils/store.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/testutils/store.go
🔇 Additional comments (5)
pkg/db/pgx_test.go (1)

1-11: LGTM!

The imports are appropriate and well-organized for database testing purposes.

pkg/db/pgx.go (4)

Line range hint 27-37: LGTM!

The function is well-structured with clear single responsibility.


39-56: LGTM!

The refactoring improves modularity by separating configuration parsing from connection establishment.


58-91: Skip duplicate comments

The SQL injection and error handling issues identified in the past reviews are still valid and should be addressed.


130-135: LGTM!

The changes are consistent with the overall refactoring pattern.

pkg/db/pgx_test.go Outdated Show resolved Hide resolved
pkg/db/pgx_test.go Outdated Show resolved Hide resolved
pkg/db/pgx.go Show resolved Hide resolved
@neekolas neekolas force-pushed the 11-26-add_support_for_creating_namespaced_databases branch from 6e15894 to 4e6c14c Compare November 27, 2024 01:29
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
pkg/db/pgx.go (2)

18-28: Enhance retry mechanism with exponential backoff

The current implementation uses a fixed 3-second sleep interval which might not be optimal for all scenarios. Consider implementing exponential backoff for more efficient retries.

+var (
+    initialRetryInterval = 1 * time.Second
+    maxRetryInterval    = 10 * time.Second
+)

 func waitUntilDBReady(ctx context.Context, db *pgxpool.Pool, waitTime time.Duration) error {
 	waitUntil := time.Now().Add(waitTime)
+	retryInterval := initialRetryInterval
 
 	err := db.Ping(ctx)
 
 	for err != nil && time.Now().Before(waitUntil) {
-		time.Sleep(3 * time.Second)
+		select {
+		case <-ctx.Done():
+			return ctx.Err()
+		case <-time.After(retryInterval):
+		}
 		err = db.Ping(ctx)
+		// Exponential backoff with maximum interval
+		retryInterval = time.Duration(
+			math.Min(float64(retryInterval*2), float64(maxRetryInterval)),
+		)
 	}
 	return err
 }

116-145: Add documentation about migration behavior

The function runs migrations but this behavior isn't documented in the function comment. Users should be aware that creating a new namespaced database will automatically run migrations.

-// Creates a new database with the given namespace if it doesn't exist and returns the full DSN for the new database.
+// NewNamespacedDB creates a new database with the given namespace if it doesn't exist,
+// runs all pending migrations, and returns a database connection.
+//
+// The function will:
+// 1. Create a new database if it doesn't exist
+// 2. Establish a connection to the new database
+// 3. Run all pending migrations
+// 4. Return the database connection
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6e15894 and 4e6c14c.

📒 Files selected for processing (3)
  • pkg/db/pgx.go (2 hunks)
  • pkg/db/pgx_test.go (1 hunks)
  • pkg/testutils/store.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/db/pgx_test.go
  • pkg/testutils/store.go
🔇 Additional comments (4)
pkg/db/pgx.go (4)

Line range hint 30-40: LGTM!

The function is well-structured, handles errors appropriately, and has a single responsibility.


42-59: LGTM!

The refactored function is well-structured with improved modularity by accepting a config object and using the new waitUntilDBReady function.


77-114: Previous security and error handling concerns remain unaddressed

The previously identified issues regarding SQL injection vulnerability and error handling are still present in this function.

Please refer to the previous review comments for detailed fixes regarding:

  1. SQL injection vulnerability when creating database
  2. Error handling using error strings instead of PostgreSQL error codes

153-157: LGTM!

The changes appropriately utilize the new parseConfig function, maintaining consistency with the modular approach.

pkg/db/pgx.go Show resolved Hide resolved
@neekolas neekolas force-pushed the 11-26-add_support_for_creating_namespaced_databases branch 3 times, most recently from de7bd67 to b97c1ee Compare November 27, 2024 01:50
@neekolas neekolas force-pushed the 11-26-add_support_for_creating_namespaced_databases branch from b97c1ee to f1897ed Compare November 27, 2024 01:52
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
pkg/db/pgx_test.go (2)

12-35: Enhance test coverage with database state verification

While the test verifies the database name, it should also validate:

  1. Database state after creation
  2. Migration status (if applicable)
  3. Basic CRUD operations
 func TestNamespacedDB(t *testing.T) {
     startingDsn := testutils.LocalTestDBDSNPrefix + "/foo" + testutils.LocalTestDBDSNSuffix
     newDBName := "xmtp_" + testutils.RandomString(24)
     // Create namespaced DB
     namespacedDB, err := NewNamespacedDB(
         context.Background(),
         startingDsn,
         newDBName,
         time.Second,
         time.Second,
     )
     t.Cleanup(func() { namespacedDB.Close() })
     require.NoError(t, err)
 
     result, err := namespacedDB.Query("SELECT current_database();")
     require.NoError(t, err)
     defer result.Close()
 
     require.True(t, result.Next())
     var dbName string
     err = result.Scan(&dbName)
     require.NoError(t, err)
     require.Equal(t, newDBName, dbName)
+
+    // Verify database state
+    _, err = namespacedDB.Exec(`
+        CREATE TABLE test_table (
+            id SERIAL PRIMARY KEY,
+            name TEXT
+        )
+    `)
+    require.NoError(t, err)
+
+    // Test basic CRUD operations
+    _, err = namespacedDB.Exec("INSERT INTO test_table (name) VALUES ($1)", "test")
+    require.NoError(t, err)
+
+    var count int
+    err = namespacedDB.QueryRow("SELECT COUNT(*) FROM test_table").Scan(&count)
+    require.NoError(t, err)
+    require.Equal(t, 1, count)
 }

1-85: Consider organizing tests into groups using t.Run

The test file would benefit from organizing related tests into groups using t.Run. This improves readability and allows for better test filtering and parallel execution.

+func TestNamespacedDB_Creation(t *testing.T) {
+    t.Run("successful creation", TestNamespacedDB)
+    t.Run("repeated creation", TestNamespaceRepeat)
+    t.Run("invalid name", TestNamespacedDBInvalidName)
+    t.Run("invalid dsn", TestNamespacedDBInvalidDSN)
+    // Add new error scenario tests here
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4e6c14c and f1897ed.

📒 Files selected for processing (3)
  • pkg/db/pgx.go (2 hunks)
  • pkg/db/pgx_test.go (1 hunks)
  • pkg/testutils/store.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/db/pgx.go
  • pkg/testutils/store.go
🔇 Additional comments (1)
pkg/db/pgx_test.go (1)

37-63: 🛠️ Refactor suggestion

Verify database state consistency between connections

The test should verify that both connections point to the same database and maintain consistent state.

     require.NoError(t, err)
     require.NotNil(t, db2)
     t.Cleanup(func() { db2.Close() })
+
+    // Verify both connections point to the same database
+    var dbName1, dbName2 string
+    err = db1.QueryRow("SELECT current_database()").Scan(&dbName1)
+    require.NoError(t, err)
+    err = db2.QueryRow("SELECT current_database()").Scan(&dbName2)
+    require.NoError(t, err)
+    require.Equal(t, dbName1, dbName2)
+
+    // Verify database state consistency
+    _, err = db1.Exec("CREATE TABLE test_table (id SERIAL PRIMARY KEY)")
+    require.NoError(t, err)
+    _, err = db2.Exec("INSERT INTO test_table DEFAULT VALUES")
+    require.NoError(t, err)
+    var count int
+    err = db1.QueryRow("SELECT COUNT(*) FROM test_table").Scan(&count)
+    require.NoError(t, err)
+    require.Equal(t, 1, count)
 }

Comment on lines +65 to +85
func TestNamespacedDBInvalidName(t *testing.T) {
_, err := NewNamespacedDB(
context.Background(),
testutils.LocalTestDBDSNPrefix+"/foo"+testutils.LocalTestDBDSNSuffix,
"invalid/name",
time.Second,
time.Second,
)
require.Error(t, err)
}

func TestNamespacedDBInvalidDSN(t *testing.T) {
_, err := NewNamespacedDB(
context.Background(),
"invalid-dsn",
"dbname",
time.Second,
time.Second,
)
require.Error(t, err)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add more error scenario tests

While the current error tests are good, consider adding these scenarios:

  1. Connection timeout
  2. Permission issues
  3. Concurrent database creation attempts
+func TestNamespacedDBTimeout(t *testing.T) {
+    _, err := NewNamespacedDB(
+        context.Background(),
+        testutils.LocalTestDBDSNPrefix+"/foo"+testutils.LocalTestDBDSNSuffix,
+        "timeout_db",
+        time.Nanosecond, // Very short timeout
+        time.Nanosecond,
+    )
+    require.Error(t, err)
+}
+
+func TestNamespacedDBConcurrent(t *testing.T) {
+    dbName := "xmtp_" + testutils.RandomString(24)
+    dsn := testutils.LocalTestDBDSNPrefix + "/foo" + testutils.LocalTestDBDSNSuffix
+
+    // Create multiple connections concurrently
+    const numConnections = 5
+    errChan := make(chan error, numConnections)
+    for i := 0; i < numConnections; i++ {
+        go func() {
+            db, err := NewNamespacedDB(
+                context.Background(),
+                dsn,
+                dbName,
+                time.Second,
+                time.Second,
+            )
+            if err == nil {
+                defer db.Close()
+            }
+            errChan <- err
+        }()
+    }
+
+    // Collect results
+    var successCount int
+    for i := 0; i < numConnections; i++ {
+        if err := <-errChan; err == nil {
+            successCount++
+        }
+    }
+
+    // At least one should succeed, others might fail due to concurrent creation
+    require.GreaterOrEqual(t, successCount, 1)
+}

Committable suggestion skipped: line range outside the PR's diff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants