Skip to content

🔥 feat: Add SurrealDB driver#1638

Merged
ReneWerner87 merged 39 commits intogofiber:mainfrom
aliyasirnac:main
May 2, 2025
Merged

🔥 feat: Add SurrealDB driver#1638
ReneWerner87 merged 39 commits intogofiber:mainfrom
aliyasirnac:main

Conversation

@aliyasirnac
Copy link
Copy Markdown
Contributor

@aliyasirnac aliyasirnac commented Apr 8, 2025

This PR adds a new storage driver for SurrealDB.

  • Implements the Storage interface
  • Supports expiration (TTL)
  • Includes full tests and benchmark
  • Adds README and configuration

Note: Since SurrealDB SDK is generic, a custom List() was implemented to fetch all entries.

Fixes #1532

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added a SurrealDB storage integration that offers robust key-value operations, expiration handling, and flexible database configuration for seamless data management.
  • Documentation

    • Introduced a new README file detailing installation, configuration, method signatures, and usage examples for the SurrealDB storage driver.
    • Updated main README to include SurrealDB storage with status badge.
  • Tests

    • Implemented comprehensive tests and performance benchmarks to ensure reliable functionality and optimal performance.

@aliyasirnac aliyasirnac requested a review from a team as a code owner April 8, 2025 00:00
@aliyasirnac aliyasirnac requested review from ReneWerner87, efectn, gaby and sixcolors and removed request for a team April 8, 2025 00:00
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 8, 2025

Walkthrough

This pull request introduces a SurrealDB storage driver for Go, featuring a new README file that outlines the driver's functionality, a configuration structure with default values, and a storage implementation that includes methods for CRUD operations and expiration handling. Additionally, a comprehensive suite of unit tests and benchmarks has been added to validate the functionality. The main components include the Storage struct with methods for managing key-value pairs in SurrealDB, a flexible Config struct with defaults, and integration instructions.

Changes

Files Change Summary
surrealdb/README.md New README detailing the storage driver functionality, including metadata, badges, usage examples, method signatures, and configuration options.
surrealdb/config.go Introduces a Config struct with fields for connection and authentication parameters, a default configuration variable ConfigDefault, and a configDefault function to merge user settings with defaults.
surrealdb/surrealdb.go Implements the Storage struct and its methods (New, Get, Set, Delete, Reset, Close, List, and Conn) for SurrealDB operations, including connection handling, key expiration logic, and CRUD functionality.
surrealdb/surrealdb_test.go New unit tests and benchmarks for the SurrealDB storage driver, covering key creation, retrieval, deletion, expiration handling, table listing, and store reset operations.
README.md Updated to add SurrealDB as a supported storage driver with a link and GitHub Actions badge.

Sequence Diagram(s)

sequenceDiagram
    participant C as Client
    participant S as Storage
    participant DB as SurrealDB

    C->>S: New(config)
    S->>DB: Connect & authenticate with config
    DB-->>S: Connection established
    S-->>C: Return Storage instance

    C->>S: Set(key, value, exp)
    S->>DB: Insert/Update record with expiration
    DB-->>S: Acknowledge operation
    S-->>C: Operation successful

    C->>S: Get(key)
    S->>DB: Retrieve record
    DB-->>S: Return record (or indicate expiration)
    S-->>C: Return value or nil
Loading

Assessment against linked issues

Objective Addressed Explanation
Implement SurrealDB storage driver (#1532)

Suggested reviewers

  • sixcolors
  • efectn
  • gaby
  • ReneWerner87

Poem

I'm a little rabbit, hopping with cheer,
New storage code sprouts, shining bright and clear.
Configs and methods in a neat parade,
Test cases and defaults perfectly laid.
With each new line, my heart does zoom—
Here's to code that makes our projects bloom!
🐇💕


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e81b66d and 722e242.

⛔ Files ignored due to path filters (1)
  • .github/workflows/benchmark.yml is excluded by !**/*.yml
📒 Files selected for processing (1)
  • surrealdb/surrealdb_test.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • surrealdb/surrealdb_test.go
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this 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 or @coderabbitai title 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
Copy Markdown
Contributor

@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

🧹 Nitpick comments (12)
surrealdb/config.go (2)

15-39: Consider secure and flexible handling of credentials.

Storing credentials (username/password) unencrypted in config structs is convenient but may pose a security risk if the config is logged or inadvertently exposed. Consider using environment variables or a secrets manager.


52-94: Exposing possible validation for config fields.

While configDefault correctly merges user overrides, consider validating critical fields. For instance, verifying that ConnectionString is a valid URL, or checking that Username and Password meet certain requirements (if any). This helps avoid edge cases at runtime.

surrealdb/surrealdb.go (4)

24-28: Consider optional TTL support at the schema level.

Storing expiration in each record is helpful, but SurrealDB may provide alternate TTL or timed record management approaches. If SurrealDB supports TTL natively, this custom approach may be streamlined further.


63-80: Log errors from the Delete call on expiration.

You gracefully delete an expired entry, but any error from s.Delete is ignored. Consider logging or handling it to catch unexpected DB issues.


109-112: Risk of losing data in multi-tenant usage.

Reset deletes the entire table. In multi-tenant or shared usage scenarios, this is destructive. Confirm whether usage contexts require typed or namespaced resets.


118-136: Handle Delete errors for expired records.

Similar to Get(), you ignore errors when deleting expired records. Logging and retrying could be beneficial if SurrealDB experiences transient errors.

surrealdb/README.md (1)

12-13: Heading style lint warning (MD036).

You’ve used emphasis instead of a heading for “Note: Requires Go 1.20 and above.” Consider changing this to a proper heading to avoid markdown lint warnings.

-**Note: Requires Go 1.20 and above**
+#### Note
+Requires Go 1.20 and above
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

12-12: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

surrealdb/surrealdb_test.go (5)

42-42: Remove extra blank line.

There's an unnecessary blank line that can be removed for better code formatting.

  require.NotEmpty(t, get)
-
}

44-48: Improve test isolation.

This test depends on data from previous tests, which could lead to flaky tests if test execution order changes. Consider adding setup code to ensure the test has its own data.

func Test_Surrealdb_ListTable(t *testing.T) {
+	// Ensure we have some data for this test
+	err := testStore.Set("list-test-key", []byte("list-test-value"), 0)
+	require.NoError(t, err)
+
	bytes, err := testStore.List()
	require.NoError(t, err)
	require.NotEmpty(t, bytes)
}

89-93: Translate comments to English.

The comments are currently in Turkish. For international collaboration and consistency, please translate them to English.

-	// Geçerli kayıt
+	// Valid record
	_ = testStore.Set("valid", []byte("123"), 0)

-	// Süresi geçen kayıt
+	// Expired record
	_ = testStore.Set("expired", []byte("456"), 1*time.Second)

73-74: Consider alternatives to time.Sleep in tests.

Using time.Sleep in tests can make them slow and potentially flaky. While sometimes necessary for testing time-dependent functionality like expiration, consider whether there are alternative approaches that could be more reliable.

If the SurrealDB driver allows it, you might consider implementing a mock time provider for testing to avoid actual sleeping.


114-117: Simplify key generation in benchmark.

The current key generation is redundant - it creates a key with format bench-key-bench-key-%d-%d. This can be simplified for better readability.

	for i := 0; i < b.N; i++ {
-		key := fmt.Sprintf("bench-key-%d-%d", i, time.Now().UnixNano())
-		err := store.Set(fmt.Sprintf("bench-key-%s", key), []byte("value"), 0)
+		key := fmt.Sprintf("bench-key-%d-%d", i, time.Now().UnixNano())
+		err := store.Set(key, []byte("value"), 0)
		if err != nil {
			b.Errorf("Set failed: %v", err)
		}
	}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9953832 and 44a21ba.

⛔ Files ignored due to path filters (4)
  • .github/workflows/benchmark.yml is excluded by !**/*.yml
  • .github/workflows/test-surrealdb.yml is excluded by !**/*.yml
  • surrealdb/go.mod is excluded by !**/*.mod
  • surrealdb/go.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (4)
  • surrealdb/README.md (1 hunks)
  • surrealdb/config.go (1 hunks)
  • surrealdb/surrealdb.go (1 hunks)
  • surrealdb/surrealdb_test.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
surrealdb/surrealdb.go (1)
surrealdb/config.go (1)
  • Config (15-39)
surrealdb/surrealdb_test.go (2)
surrealdb/surrealdb.go (2)
  • Storage (12-15)
  • New (32-61)
surrealdb/config.go (1)
  • ConfigDefault (41-50)
🪛 markdownlint-cli2 (0.17.2)
surrealdb/README.md

12-12: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

🔇 Additional comments (7)
surrealdb/config.go (2)

3-14: Well-documented purpose and usage.

The doc comments are clear and thorough, which is helpful for new users.


41-50: Good practice providing sensible defaults.

Supplying a default configuration is handy for rapid development. The chosen defaults look appropriate for a local development environment.

surrealdb/surrealdb.go (4)

12-15: Check handling of an empty table name.

Currently, there's no safeguard if cfg.DefaultTable turns out empty. SurrealDB calls may fail downstream. It might be worth validating the table name before usage.


82-98: Confirm record overwrite behavior.

surrealdb.Create might fail or duplicate if the record already exists. If you intend to overwrite an existing record, check whether you need Update or an upsert-like method.


100-107: Error handling for nonexistent keys.

Deleting a key that doesn't exist is not an error here, which is acceptable. Just ensure this behavior is intentional and documented for users who expect an error on missing keys.


114-116: Straightforward resource cleanup.

Your Close() method correctly calls db.Close(). This is clean and expected.

surrealdb/surrealdb_test.go (1)

1-121: Good test coverage of SurrealDB storage driver functionality.

The test file provides comprehensive coverage of the SurrealDB storage driver's functionality, including CRUD operations, expiration handling, and performance benchmarking. The tests are well-structured and use appropriate assertions.

Comment thread surrealdb/surrealdb.go Outdated
Comment thread surrealdb/README.md Outdated
Comment thread surrealdb/surrealdb_test.go Outdated
Comment on lines +87 to +94
_ = testStore.Reset()

// Geçerli kayıt
_ = testStore.Set("valid", []byte("123"), 0)

// Süresi geçen kayıt
_ = testStore.Set("expired", []byte("456"), 1*time.Second)
time.Sleep(2 * time.Second)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Handle returned errors in test setup.

Error values from Reset() and Set() calls are being ignored. Even in tests, it's good practice to check these errors to ensure proper test setup.

func Test_Surrealdb_ListSkipsExpired(t *testing.T) {
-	_ = testStore.Reset()
+	err := testStore.Reset()
+	require.NoError(t, err)

	// Geçerli kayıt
-	_ = testStore.Set("valid", []byte("123"), 0)
+	err = testStore.Set("valid", []byte("123"), 0)
+	require.NoError(t, err)

	// Süresi geçen kayıt
-	_ = testStore.Set("expired", []byte("456"), 1*time.Second)
+	err = testStore.Set("expired", []byte("456"), 1*time.Second)
+	require.NoError(t, err)
	time.Sleep(2 * time.Second)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_ = testStore.Reset()
// Geçerli kayıt
_ = testStore.Set("valid", []byte("123"), 0)
// Süresi geçen kayıt
_ = testStore.Set("expired", []byte("456"), 1*time.Second)
time.Sleep(2 * time.Second)
func Test_Surrealdb_ListSkipsExpired(t *testing.T) {
err := testStore.Reset()
require.NoError(t, err)
// Geçerli kayıt
err = testStore.Set("valid", []byte("123"), 0)
require.NoError(t, err)
// Süresi geçen kayıt
err = testStore.Set("expired", []byte("456"), 1*time.Second)
require.NoError(t, err)
time.Sleep(2 * time.Second)
}

Copy link
Copy Markdown
Member

@gaby gaby left a comment

Choose a reason for hiding this comment

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

Looks good overall, just a few comments here and there. Will re-review once those are done. Thanks! 💪

Comment thread .github/workflows/test-surrealdb.yml Outdated
Comment thread .github/workflows/test-surrealdb.yml Outdated
Comment thread surrealdb/README.md Outdated
Comment thread surrealdb/README.md Outdated
Comment thread surrealdb/README.md Outdated
Comment thread surrealdb/surrealdb_test.go
Comment thread surrealdb/surrealdb_test.go Outdated
Comment thread surrealdb/surrealdb_test.go
Comment thread surrealdb/README.md Outdated
Comment thread surrealdb/surrealdb.go
@aliyasirnac
Copy link
Copy Markdown
Contributor Author

@gaby thanks for your feedbacks

@aliyasirnac aliyasirnac requested a review from gaby April 8, 2025 02:15
Comment thread .github/workflows/test-surrealdb.yml Outdated
Comment thread surrealdb/README.md Outdated
Comment thread surrealdb/go.mod Outdated
Comment thread surrealdb/surrealdb_test.go Outdated
- Replaced Create with Upsert to allow overwriting existing keys
- Removed trailing spaces from line 2 and 3 in README
- Addressed other code review suggestions (naming, consistency, etc.)
@gaby gaby requested a review from Copilot April 25, 2025 22:37
@gaby
Copy link
Copy Markdown
Member

gaby commented Apr 25, 2025

@aliyasirnac What's left on this PR?

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds a new SurrealDB storage driver for the project, implementing the Storage interface with support for key-value operations, TTL expiration, and integrations with tests, benchmarks, and documentation updates.

  • Implements Storage interface methods (Get, Set, Delete, List, Reset, Close) for SurrealDB.
  • Adds comprehensive tests and benchmarks to validate driver functionality.
  • Updates README and workflow configurations to include SurrealDB support.

Reviewed Changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
surrealdb/surrealdb_test.go Adds tests for CRUD, expiration, list behavior, and benchmarks
surrealdb/surrealdb.go Implements the storage driver using SurrealDB SDK with panic on errors
surrealdb/config.go Provides configuration defaults and overrides
surrealdb/README.md Documents usage, configuration, and examples for the new driver
README.md Updates main README to reference the new SurrealDB driver
.github/workflows/test-surrealdb.yml Adds CI workflow to test the SurrealDB driver
.github/workflows/benchmark.yml Integrates SurrealDB into the benchmark workflow
Files not reviewed (1)
  • surrealdb/go.mod: Language not supported

Comment thread surrealdb/surrealdb_test.go
Copy link
Copy Markdown
Contributor

@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: 0

🧹 Nitpick comments (3)
README.md (3)

81-82: Maintain alphabetical order in storage implementations list

The new SurrealDB entry should appear before Valkey to keep the list sorted alphabetically by driver name.

- - [Valkey](./valkey/README.md) <a href="/gofiber/storage/actions?query=workflow%3A%22Tests+valkey%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-valkey.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
- - [SurrealDB](./surrealdb/README.md) <a href="/gofiber/storage/actions?query=workflow%3A%22Tests+surrealdb%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-surrealdb.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
+ - [SurrealDB](./surrealdb/README.md) <a href="/gofiber/storage/actions?query=workflow%3A%22Tests+surrealdb%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-surrealdb.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" alt="SurrealDB Tests Status" /> </a>
+ - [Valkey](./valkey/README.md) <a href="/gofiber/storage/actions?query=workflow%3A%22Tests+valkey%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-valkey.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" alt="Valkey Tests Status" /> </a>
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

81-81: Images should have alternate text (alt text)
null

(MD045, no-alt-text)


82-82: Images should have alternate text (alt text)
null

(MD045, no-alt-text)


81-81: Add alt attribute to Valkey badge for accessibility

The <img> tag on this line is missing an alt attribute, which is required for accessibility and to satisfy markdownlint (MD045).

- <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-valkey.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" />
+ <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-valkey.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" alt="Valkey Tests Status" />
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

81-81: Images should have alternate text (alt text)
null

(MD045, no-alt-text)


82-82: Add alt attribute to SurrealDB badge for accessibility

Similarly, this <img> tag needs an alt attribute to improve accessibility and satisfy markdownlint (MD045).

- <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-surrealdb.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" />
+ <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-surrealdb.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" alt="SurrealDB Tests Status" />
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

82-82: Images should have alternate text (alt text)
null

(MD045, no-alt-text)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8deabd1 and 28c3e18.

⛔ Files ignored due to path filters (1)
  • .github/workflows/benchmark.yml is excluded by !**/*.yml
📒 Files selected for processing (1)
  • README.md (1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
README.md

81-81: Images should have alternate text (alt text)
null

(MD045, no-alt-text)


82-82: Images should have alternate text (alt text)
null

(MD045, no-alt-text)

@aliyasirnac
Copy link
Copy Markdown
Contributor Author

@aliyasirnac What's left on this PR?

I don't have much of an idea. KV and in-memory are not currently supported in the official sdk, so I couldn't add them. Some repositories had garbage collectors, maybe I can add them and maybe we can add and refactor the bots' suggestions.

@aliyasirnac aliyasirnac requested a review from gaby April 26, 2025 08:14
Copy link
Copy Markdown
Contributor

@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: 0

♻️ Duplicate comments (2)
surrealdb/config.go (2)

40-46: Consider removing or better documenting the commented-out Mode field.

The commented-out Mode field suggests planned support for different operational modes, but it's currently not implemented. From the PR objectives, I understand that SurrealDB SDK doesn't support embedded database modes (KV and memory) yet.

Instead of keeping commented code, consider either:

  1. Remove the commented field entirely until support is added
  2. Add a more detailed comment explaining why it's commented out and any plans for future implementation

If you decide to keep it commented, consider adding a reference to the SDK issue tracking this limitation.


100-102: Missing Mode field initialization in configDefault.

The configDefault function doesn't set the default Mode if it's not provided, unlike other fields.

	if cfg.DefaultTable == "" {
		cfg.DefaultTable = ConfigDefault.DefaultTable
	}

+	if cfg.Mode == "" {
+		cfg.Mode = ConfigDefault.Mode
+	}

	return cfg
🧹 Nitpick comments (3)
surrealdb/config.go (3)

48-57: Add a security note about default credentials.

The default configuration uses "root"/"root" as credentials, which is fine for development but could be dangerous if used in production. Consider adding a comment warning users to change these values in production environments.

var ConfigDefault = Config{
	ConnectionString: "ws://localhost:8000",
	Namespace:        "fiber_storage",
	Database:         "fiber_storage",
	Username:         "root", // Warning: Change in production
	Password:         "root", // Warning: Change in production
	Access:           "full",
	Scope:            "all",
	DefaultTable:     "fiber_storage",
+	// Warning: Default credentials are for development only.
+	// Use secure credentials in production environments.
}

59-105: Consider adding basic configuration validation.

The configDefault function merges default values but doesn't validate the correctness of the configuration. Consider adding basic validation, such as checking if the connection string is a valid URL or if the table name contains invalid characters.

This would help users identify configuration issues early before attempting to establish a connection.

Here's an example implementation:

func configDefault(config ...Config) Config {
	// Return default config if nothing provided
	if len(config) < 1 {
		return ConfigDefault
	}

	// Override default config
	cfg := config[0]

	// Set default values
	if cfg.ConnectionString == "" {
		cfg.ConnectionString = ConfigDefault.ConnectionString
	}
	// ...other default settings...
	
+	// Basic validation
+	if !strings.HasPrefix(cfg.ConnectionString, "ws://") && !strings.HasPrefix(cfg.ConnectionString, "wss://") && 
+	   !strings.HasPrefix(cfg.ConnectionString, "http://") && !strings.HasPrefix(cfg.ConnectionString, "https://") {
+		// Don't fail here, just log a warning
+		log.Printf("Warning: ConnectionString %s doesn't start with ws://, wss://, http:// or https://", cfg.ConnectionString)
+	}
+	
+	// Additional validations could be added here

	return cfg
}

16-18: Clarify the preferred connection protocol.

Based on the PR discussions, WebSocket connections work better than HTTP for SurrealDB. Consider enhancing the documentation to recommend using WebSocket connections (ws:// or wss://) over HTTP connections.

	// The connection URL to connect to SurrealDB
+	// WebSocket connections (ws:// or wss://) are recommended over HTTP for better reliability
	ConnectionString string
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28c3e18 and caa5078.

📒 Files selected for processing (3)
  • surrealdb/README.md (1 hunks)
  • surrealdb/config.go (1 hunks)
  • surrealdb/surrealdb_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • surrealdb/README.md
  • surrealdb/surrealdb_test.go

Copy link
Copy Markdown
Contributor

@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: 2

♻️ Duplicate comments (2)
surrealdb/config.go (2)

20-44: The Config struct is missing the Mode field mentioned in previous reviews.

According to past review comments, the Config struct should include a Mode field with supported values like "default", "memory", and "kv" to handle different SurrealDB operating modes.

Add a Mode field to the Config struct:

type Config struct {
	// WebSocket connections (ws:// or wss://) are recommended over HTTP for better reliability
	ConnectionString string

	// The namespace to be used in SurrealDB
	Namespace string

	// The database to be used within the specified namespace
	Database string

	// The application username to connect to SurrealDB
	Username string

	// The application password to connect to SurrealDB
	Password string

	// Optional access token or access type
	Access string

	// Optional scope for scoped logins (e.g., user-defined scopes)
	Scope string

	// The default table used to store key-value records
	DefaultTable string
+
+	// Mode defines the operating mode ("default", "memory", "kv")
+	Mode string
}

94-96: Missing Mode field initialization in configDefault.

The configDefault function doesn't set the default Mode if it's not provided, unlike other fields.

Add the missing default assignment:

	if cfg.DefaultTable == "" {
		cfg.DefaultTable = ConfigDefault.DefaultTable
	}
+
+	if cfg.Mode == "" {
+		cfg.Mode = ConfigDefault.Mode
+	}
🧹 Nitpick comments (3)
surrealdb/config.go (3)

46-55: Default credentials could pose a security risk.

The default credentials (root/root) might be used in production if not explicitly overridden. Consider using empty strings as defaults to force explicit configuration.

var ConfigDefault = Config{
	ConnectionString: "ws://localhost:8000",
	Namespace:        "fiber_storage",
	Database:         "fiber_storage",
-	Username:         "root",
-	Password:         "root",
+	Username:         "",
+	Password:         "",
	Access:           "full",
	Scope:            "all",
	DefaultTable:     "fiber_storage",
}

Then update the New function in surrealdb.go to check if credentials are empty and handle accordingly.


3-6: Import statements could be more comprehensive.

Consider adding fmt for improved error formatting if implementing validation, and organize imports alphabetically.

import (
+	"fmt"
	"log"
	"strings"
)

57-104: Missing configuration options for timeouts and connection pooling.

The configuration lacks options for connection timeouts, retry policies, and connection pooling, which are important for production deployments.

Consider extending the Config struct to include these additional options:

type Config struct {
	// ... existing fields ...

+	// ConnectionTimeout specifies the maximum time to wait for a connection
+	ConnectionTimeout time.Duration
+
+	// MaxConnections specifies the maximum number of connections in the pool
+	MaxConnections int
+
+	// RetryAttempts specifies the number of retry attempts for failed operations
+	RetryAttempts int
}

Then update the corresponding defaults in ConfigDefault and initialization in configDefault.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between caa5078 and 0dea23f.

📒 Files selected for processing (1)
  • surrealdb/config.go (1 hunks)

Comment thread surrealdb/config.go
Comment thread surrealdb/config.go
Comment thread surrealdb/go.mod
Copy link
Copy Markdown
Contributor

@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

♻️ Duplicate comments (1)
surrealdb/surrealdb.go (1)

50-57: 🛠️ Refactor suggestion

Add cleanup on authentication failure.

If authentication fails, the connection should be closed to prevent resource leaks.

 	token, err := db.SignIn(authData)
 	if err != nil {
-		panic(err)
+		// Close the connection to prevent leaks
+		_ = db.Close()
+		return nil, fmt.Errorf("failed to sign in: %w", err)
 	}
 
 	if err = db.Authenticate(token); err != nil {
-		panic(err)
+		// Close the connection to prevent leaks
+		_ = db.Close()
+		return nil, fmt.Errorf("failed to authenticate: %w", err)
 	}
🧹 Nitpick comments (9)
surrealdb/config.go (2)

103-106: Connection string validation could be improved.

Currently, invalid connection strings only trigger a warning log. Consider providing a validation method that users can call to check configuration validity before initializing storage.

// Add a validation method to Config struct
+
+// Validate checks if the configuration is valid and returns an error if not
+func (c *Config) Validate() error {
+	if !strings.HasPrefix(c.ConnectionString, "ws://") && !strings.HasPrefix(c.ConnectionString, "wss://") &&
+		!strings.HasPrefix(c.ConnectionString, "http://") && !strings.HasPrefix(c.ConnectionString, "https://") {
+		return fmt.Errorf("invalid connection string format: %s", c.ConnectionString)
+	}
+	return nil
+}

This would allow users to validate their configuration before using it, and the New function in surrealdb.go could call this validation method.


108-110: Consider adding an upper bound check for GCInterval.

While there's a check for a positive GCInterval, extremely large values might not be practical and could impact performance.

-	if int(cfg.GCInterval.Seconds()) <= 0 {
+	if int(cfg.GCInterval.Seconds()) <= 0 {
 		cfg.GCInterval = ConfigDefault.GCInterval
+	} else if cfg.GCInterval > time.Hour {
+		log.Printf("Warning: GCInterval is very large (%v). This might impact performance.", cfg.GCInterval)
 	}
surrealdb/surrealdb.go (2)

165-176: Improve error handling in cleanupExpired method.

Currently, errors from Delete operations in the cleanup process are silently ignored, which might hide issues.

 func (s *Storage) cleanupExpired() {
 	records, err := surrealdb.Select[[]model, models.Table](s.db, models.Table(s.table))
 	if err != nil {
+		// Consider logging the error or having a way to monitor cleanup failures
 		return
 	}
 	now := time.Now().Unix()
 	for _, item := range *records {
 		if item.Exp > 0 && now > item.Exp {
-			_ = s.Delete(item.Key)
+			if err := s.Delete(item.Key); err != nil {
+				// Consider logging errors during cleanup
+				// log.Printf("Error deleting expired key %s: %v", item.Key, err)
+			}
 		}
 	}
 }

12-17: Consider adding mutex for concurrent access protection.

The Storage struct doesn't protect against concurrent access to the DB connection, which could lead to race conditions in a multi-goroutine environment.

Add a mutex to protect concurrent access:

 type Storage struct {
 	db       *surrealdb.DB
 	table    string
 	stopGC   chan struct{}
 	interval time.Duration
+	mu       sync.RWMutex  // Protects concurrent access to db
 }

Then use the mutex in methods that access the db field:

func (s *Storage) Get(key string) ([]byte, error) {
	if len(key) == 0 {
		return nil, errors.New("key is required")
	}

	s.mu.RLock()
	defer s.mu.RUnlock()
	
	recordID := models.NewRecordID(s.table, key)
	// ... rest of the method
}
surrealdb/surrealdb_test.go (5)

169-172: Handle returned errors in test setup.

Error values from Set() calls are being ignored. Even in tests, it's good practice to check these errors to ensure proper test setup.

-	_ = testStore.Set("valid", []byte("123"), 0)
+	err = testStore.Set("valid", []byte("123"), 0)
+	require.NoError(t, err)

-	_ = testStore.Set("expired", []byte("456"), 1*time.Second)
+	err = testStore.Set("expired", []byte("456"), 1*time.Second)
+	require.NoError(t, err)
 	time.Sleep(2 * time.Second)

172-172: Replace fixed sleep with a more reliable approach.

Using a fixed sleep duration can lead to flaky tests depending on the test environment. Consider using require.Eventually as implemented in other tests.

-	time.Sleep(2 * time.Second)
+	require.Eventually(t, func() bool {
+		val, _ := testStore.Get("expired")
+		return val == nil
+	}, 3*time.Second, 100*time.Millisecond)

This approach is more reliable and consistent with the Test_Surrealdb_GetExpired test implementation.


214-219: Handle errors for each operation in benchmark.

The error check is outside the loop and will only check the last iteration. For comprehensive benchmarking, check errors inside the loop.

 	for i := 0; i < b.N; i++ {
-		err = testStore.Set("john", []byte("doe"), 0)
+		if err = testStore.Set("john", []byte("doe"), 0); err != nil {
+			b.Fatal(err)
+		}
 	}

-	require.NoError(b, err)

232-237: Handle errors for each operation in Get benchmark.

Similar to the Set benchmark, the error check is outside the loop and will only check the last iteration.

 	for i := 0; i < b.N; i++ {
-		_, err = testStore.Get("john")
+		_, err = testStore.Get("john")
+		if err != nil {
+			b.Fatal(err)
+		}
 	}

-	require.NoError(b, err)

247-253: Handle errors for both operations in SetAndDelete benchmark.

The error from Set() is ignored, and the error check for Delete() is outside the loop, only checking the last iteration.

 	for i := 0; i < b.N; i++ {
-		testStore.Set("john", []byte("doe"), 0)
-		err = testStore.Delete("john")
+		if err = testStore.Set("john", []byte("doe"), 0); err != nil {
+			b.Fatal(err)
+		}
+		if err = testStore.Delete("john"); err != nil {
+			b.Fatal(err)
+		}
 	}

-	require.NoError(b, err)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0dea23f and e81b66d.

⛔ Files ignored due to path filters (3)
  • .github/workflows/benchmark.yml is excluded by !**/*.yml
  • surrealdb/go.mod is excluded by !**/*.mod
  • surrealdb/go.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (4)
  • surrealdb/README.md (1 hunks)
  • surrealdb/config.go (1 hunks)
  • surrealdb/surrealdb.go (1 hunks)
  • surrealdb/surrealdb_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • surrealdb/README.md
🧰 Additional context used
🧠 Learnings (1)
surrealdb/surrealdb_test.go (2)
Learnt from: sixcolors
PR: gofiber/fiber#3016
File: middleware/session/middleware_test.go:190-191
Timestamp: 2024-09-25T17:05:06.991Z
Learning: When testing session `IdleTimeout` expiration, it's acceptable to use `time.Sleep` to simulate the passage of time in tests.
Learnt from: sixcolors
PR: gofiber/fiber#3016
File: middleware/session/middleware_test.go:190-191
Timestamp: 2024-10-12T10:01:44.206Z
Learning: When testing session `IdleTimeout` expiration, it's acceptable to use `time.Sleep` to simulate the passage of time in tests.
🧬 Code Graph Analysis (2)
surrealdb/surrealdb.go (1)
surrealdb/config.go (1)
  • Config (21-48)
surrealdb/surrealdb_test.go (2)
surrealdb/surrealdb.go (2)
  • Storage (12-17)
  • New (34-68)
surrealdb/config.go (1)
  • Config (21-48)

Comment thread surrealdb/surrealdb.go
Comment thread surrealdb/surrealdb_test.go Outdated
Copy link
Copy Markdown
Member

@gaby gaby left a comment

Choose a reason for hiding this comment

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

👍 LGTM

@ReneWerner87 ReneWerner87 merged commit 121ab15 into gofiber:main May 2, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🚀 [Feature]: Support for SurrealDB

6 participants