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

NBD integration into controller and replica #1109

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

Toutou98
Copy link

@Toutou98 Toutou98 commented May 10, 2024

Which issues this PR references

Issues: longhorn/longhorn#6590 (comment)


longhorn/longhorn#5002 (comment)


longhorn/longhorn#5374 (comment)

What this PR does

This PR integrates the Network Block Device (NBD) protocol as an option into both the kernel to controller (instead of iSCSI) and controller to replica communications (instead of the custom Longhorn engine protocol). NBD is fairly standard, available prebuilt in most Linux distributions. The current version also supports multiple concurrent connections, which this PR takes advantage of.

The benefit is increased performance, especially at the frontend. In my tests, using a fairly recent PC hardware configuration, I found tgtd to be a major bottleneck, limiting the frontend (kernel to controller) R/W IOPS to ~50k, whereas this number almost increases 10-fold with NBD and multiple concurrent connections. Similarly, the backend (controller to replica) performance improves, although there is probably still a lot of room for improvement inside the controller. Test setup and performance results are given below.

This PR includes all code changes, including additional parameters to the engine binaries to enable NBD and control the number of concurrent connections. It also includes some necessary additions to the container produced.

Test setup and results

Hardware configuration:

  • CPU: Ryzen7 5700X @3.40Ghz
  • DRAM: 32GB DDR4 3600MT/s
  • Disk: Ramdisk 10GB
  • OS: Ubuntu 22.04
  • Kernel: Linux 6.5.0-14-generic

Three series of tests were conducted, using fio to load the longhorn device:

  • Frontend test to measure the performance up to the controller
  • Dataconn test to measure the performance up to the replica (including the network overhead)
  • End to end test to measure the performance when using a disk (used a Ramdisk to factor out the device speed)

Below is a table of results, showing the best results with NBD that were achieved using 16 connections.

benchmarks

fio configuration file:

[global]
group_reporting
ramp_time=5
time_based=1
runtime=60
direct=1
ioengine=libaio
filename=/dev/longhorn/vol-name

[iops-write]
gtod_reduce=1
blocksize=4K
iodepth=16
numjobs=16
rw=randwrite

[iops-read]
stonewall
wait_for=iops-write
gtod_reduce=1
blocksize=4K
iodepth=16
numjobs=16

Notes for reviewer

To be able to replicate the results you need libnbd-dev/libnbd-devel (v1.13.1) and nbd-client to be installed in the host system. Also you need the nbd kernel module to be installed in the kernel (available on Ubuntu 22.04).

I have modified the launch-simple-longhorn script to use nbd both at the frontend and backend with a configurable number of parallel connections. The command used to run the engine with 16 frontend connections and 16 backend connections is:

sudo docker run --privileged --net=host -v /lib/modules/<kernel>:/lib/modules/<kernel>  -v /dev:/host/dev -v /proc:/host/proc -v /volume longhornio/longhorn-engine:<tag> launch-simple-longhorn vol-name 10g nbd 16 16

Additional information or context

Frontend implementation details:

  • The client side uses the nbd kernel module that Linux already provides.
  • The server side within the controller, uses a modified version of go-nbd (modified for concurrency).

Backend implementation details:

  • The client side uses libnbd (development version).
  • The server side uses the same go-nbd-based implementation as the controller.

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced new command-line flags for configuring frontend streams and enabling NBD (Network Block Device) support.
    • Added support for the libnbd library, enhancing the application's capabilities for network block device operations.
    • New NBD frontend and server implementations allow for better management of block storage volumes.
  • Bug Fixes

    • Improved error handling and validation for new command-line flags.
  • Documentation

    • Updated README files and added license information for new dependencies.
  • Chores

    • Updated dependency management to include new libraries and versions.

Copy link

mergify bot commented May 10, 2024

This pull request is now in conflict. Could you fix it @Toutou98? 🙏

Copy link

This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.

@github-actions github-actions bot added the stale label Jun 25, 2024
@derekbit
Copy link
Member

What we can do for the feature later

  • Compare the performance of new data paths based on NBD and UBLK.
  • Analyze the advantages and disadvantages of each data path and their use cases.

cc @shuo-wu @PhanLe1010 @c3y1huang @mantissahz @Vicente-Cheng @WebberHuang1118 @innobead

@github-actions github-actions bot removed the stale label Jun 26, 2024
Copy link

This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.

@github-actions github-actions bot added the stale label Aug 10, 2024
Copy link

This PR was closed because it has been stalled for 10 days with no activity.

@github-actions github-actions bot closed this Aug 21, 2024
@PhanLe1010
Copy link
Contributor

Reopening. I am actively investigate this PR

@PhanLe1010 PhanLe1010 reopened this Aug 23, 2024
Copy link

mergify bot commented Aug 23, 2024

This pull request is now in conflict. Could you fix it @Toutou98? 🙏

@github-actions github-actions bot removed the stale label Aug 24, 2024
Copy link

github-actions bot commented Oct 8, 2024

This PR is stale because it has been open for 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.

@github-actions github-actions bot added the stale label Oct 8, 2024
Copy link

This PR was closed because it has been stalled for 10 days with no activity.

@github-actions github-actions bot closed this Oct 18, 2024
@derekbit derekbit removed the stale label Oct 18, 2024
@derekbit derekbit reopened this Oct 18, 2024
Copy link

mergify bot commented Oct 18, 2024

This pull request is now in conflict. Could you fix it @Toutou98? 🙏

Copy link

github-actions bot commented Dec 3, 2024

This PR is stale because it has been open for 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.

@github-actions github-actions bot added the stale label Dec 3, 2024
Copy link

coderabbitai bot commented Dec 3, 2024

Walkthrough

The pull request introduces significant enhancements to the application by adding support for the Network Block Device (NBD) protocol. Key changes include modifications to Dockerfiles for building necessary libraries, updates to command-line interfaces for configuring NBD and frontend streams, and the introduction of new backend types and methods for handling NBD operations. Additionally, new files for managing NBD connections and operations are added, alongside updates to existing interfaces and structures to accommodate the new functionality.

Changes

File Path Change Summary
Dockerfile.dapper, package/Dockerfile Added installation commands for libnbd and libqcow libraries, including necessary dependencies and configuration options.
app/cmd/controller.go, app/cmd/replica.go Introduced new command-line flags frontend-streams and nbd-enabled for configuring frontend connections and NBD server settings.
go.mod Added new dependencies: github.com/pmorjan/kmod, github.com/pojntfx/go-nbd, and golang.org/x/sync. Included a replacement directive for go-nbd.
pkg/backend/dynamic/dynamic.go, pkg/backend/file/file.go, pkg/backend/remote/remote.go Updated Create method signatures to include nbdEnabled parameter, affecting backend instantiation and control flow.
pkg/controller/control.go, pkg/controller/init_frontend.go Updated Controller struct and methods to include frontendStreams and nbdEnabled parameters for enhanced configuration management.
pkg/dataconn/nbd_client.go, pkg/dataconn/nbd_server.go Introduced new types and methods for managing NBD client and server functionality, including connection handling and data operations.
vendor/github.com/pmorjan/kmod/* Added a new package for kernel module management with necessary types, methods, and error handling.
vendor/github.com/pojntfx/go-nbd/* Introduced a new package for NBD protocol handling, including interfaces and methods for backend operations, connection management, and asynchronous I/O.
scripts/build Updated build script to simplify linking process by removing static linking flags.
vendor/modules.txt Updated to reflect new dependencies and modifications, including explicit Go versions for compatibility.

Poem

In fields of code, we hop and play,
New features bloom, brightening the way.
With NBD's grace, we dance and cheer,
Frontend streams flow, our path is clear!
So let’s build and run, with joy we’ll sing,
For every change, a new adventure brings! 🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 golangci-lint (1.62.2)

level=error msg="Running error: context loading failed: failed to load packages: failed to load packages: failed to load with go/packages: err: exit status 1: stderr: go: cannot load module . listed in go.work file: open go.mod: no such file or directory\ngo: cannot load module cmd/modprobe listed in go.work file: open cmd/modprobe/go.mod: no such file or directory\n"


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

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

🧹 Outside diff range and nitpick comments (24)
package/Dockerfile (2)

30-30: Add progress indicators or suppress output when using wget in Dockerfile.

To improve build log readability and avoid excessive output, consider adding --progress=dot:giga to wget commands or suppress output with -q or --no-verbose.

Apply this diff:

 RUN wget -O - https://s3-us-west-1.amazonaws.com/rancher-longhorn/libqcow-alpha-20181117.tar.gz | tar xvzf - -C /usr/src
+RUN wget --progress=dot:giga -O - https://s3-us-west-1.amazonaws.com/rancher-longhorn/libqcow-alpha-20181117.tar.gz | tar xvzf - -C /usr/src

 ...

 RUN cd /usr/src && \
-    wget -O - https://download.libguestfs.org/libnbd/1.13-development/libnbd-1.13.1.tar.gz | tar -xzf - && \
+    wget --progress=dot:giga -O - https://download.libguestfs.org/libnbd/1.13-development/libnbd-1.13.1.tar.gz | tar -xzf - && \
     cd libnbd-1.13.1 && \
     ./configure --disable-ocaml --disable-python --disable-golang; \
     make; \

Also applies to: 38-38

🧰 Tools
🪛 GitHub Check: CodeFactor

[notice] 30-30: package/Dockerfile#L30
Avoid use of wget without progress bar. Use wget --progress=dot:giga <url>. Or consider using -q or -nv (shorthands for --quiet or --no-verbose). (DL3047)


37-37: Specify package versions and clean up after zypper install.

To ensure consistent builds and reduce image size, specify exact package versions when installing with zypper and clean up the package cache after installation.

Apply this diff:

 RUN zypper -n install libxml2-devel nbd
+RUN zypper -n install libxml2-devel=<version> nbd=<version> && \
+    zypper clean --all && \
+    rm -rf /var/cache/zypp/*

 ...

 RUN zypper -n install nfs-client nfs4-acl-tools cifs-utils libaio1 sg3_utils nbd \
     iputils iproute2 qemu-tools e2fsprogs && \
-    rm -rf /var/cache/zypp/*
+    zypper clean --all && \
+    rm -rf /var/cache/zypp/*

Replace <version> with the specific versions required.

Also applies to: 59-59

🧰 Tools
🪛 GitHub Check: CodeFactor

[warning] 37-37: package/Dockerfile#L37
Specify version with zypper install -y <package>=<version>. (DL3037)


[warning] 37-37: package/Dockerfile#L37
zypper clean missing after zypper use. (DL3036)

pkg/frontend/nbd/frontend.go (4)

55-57: Enhance Error Context in Startup Method

In the Startup method, the error returned by startNbdServer lacks contextual information, which can make debugging difficult. Consider wrapping the error with additional context.

Apply this diff:

func (n *Nbd) Startup(rwu types.ReaderWriterUnmapperAt) error {
    if err := n.startNbdServer(rwu); err != nil {
-       return err
+       return fmt.Errorf("failed to start NBD server: %w", err)
    }
    n.isUp = true
    // ...
}

83-91: Check for Errors When Removing Existing Symlink

When removing an existing symlink at devicePath, errors from os.Remove are ignored. This could lead to hidden issues if the file cannot be removed.

Apply this diff:

if _, err := os.Lstat(devicePath); err == nil {
-   os.Remove(devicePath)
+   if err := os.Remove(devicePath); err != nil {
+       return fmt.Errorf("failed to remove existing device symlink: %w", err)
+   }
}

96-107: Limit Hardcoded NBD Device Iteration

In getNextFreeNbdDevice, the loop checks only 16 devices. If more NBD devices are available, they won't be utilized. Consider retrieving the maximum number of NBD devices dynamically or making the limit configurable.

Apply this diff:

-func (n *Nbd) getNextFreeNbdDevice() (string, error) {
-   for i := 0; i < 16; i++ {
+func (n *Nbd) getNextFreeNbdDevice(maxDevices int) (string, error) {
+   for i := 0; i < maxDevices; i++ {
        nbdDevicePath := fmt.Sprintf("/dev/nbd%d", i)
        // ...
    }
    return "", fmt.Errorf("no more nbd devices")
}

And update the call site in Startup:

 n.nbdDevicePath, err = n.getNextFreeNbdDevice()
+// Potentially pass the desired maximum devices

209-211: Recover Panics in Goroutines

In handleServerConnection, the deferred function captures panics but only logs them. This may hide critical issues. Consider logging at an appropriate level and possibly propagating the error.

vendor/libguestfs.org/libnbd/bindings.go (1)

1429-1434: Unused or Incomplete Struct Definition

The PreadOptargs struct is defined but may not be fully implemented or utilized in the code.

Review the usage of PreadOptargs to ensure it's fully integrated or remove it if unnecessary.

vendor/github.com/pojntfx/go-nbd/pkg/backend/backend.go (1)

5-11: LGTM! Consider adding interface documentation.

The Backend interface is well-designed, combining standard io interfaces with essential block device operations. It's thread-safe by design through the use of ReaderAt/WriterAt.

Consider adding GoDoc comments to document:

  • The interface's purpose in NBD context
  • Expected behavior of Size() and Sync()
  • Thread-safety guarantees
+// Backend defines the interface for NBD backend implementations.
+// It provides thread-safe read/write operations and block device management.
 type Backend interface {
 	io.ReaderAt
 	io.WriterAt
 
+	// Size returns the total size of the backend in bytes
 	Size() (int64, error)
+	// Sync ensures all pending writes are persisted
 	Sync() error
 }
vendor/golang.org/x/sync/errgroup/pre_go120.go (2)

12-15: Consider logging dropped error cause in pre-Go 1.20 implementation

While the implementation provides compatible behavior, it silently drops the error cause which could impact debugging capabilities in older Go versions.

Consider adding debug logging when error cause is dropped:

 func withCancelCause(parent context.Context) (context.Context, func(error)) {
 	ctx, cancel := context.WithCancel(parent)
-	return ctx, func(error) { cancel() }
+	return ctx, func(err error) {
+		if err != nil {
+			// Log dropped error cause for debugging
+			log.Printf("Context cancelled with cause (dropped in pre-Go 1.20): %v", err)
+		}
+		cancel()
+	}
 }

1-15: Consider documenting NBD error handling strategy

These foundational pieces handle the low-level NBD operations and error propagation. To ensure reliable NBD integration:

  1. Document how NBD-specific errors are propagated through the error cause chain
  2. Consider adding error codes/types specific to NBD operations
  3. Establish guidelines for handling NBD connection failures and recovery

Would you like help creating this documentation?

vendor/github.com/pojntfx/go-nbd/pkg/protocol/transmission.go (1)

4-14: Consider documenting NBD protocol version compatibility

The magic numbers and constants align with the NBD protocol specification. However, it would be helpful to document which NBD protocol version this implementation targets.

Consider adding a comment block at the top of the constants section:

+// NBD Protocol v2.9.8 constants
+// Reference: https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md
vendor/github.com/pojntfx/go-nbd/pkg/protocol/negotiation.go (1)

3-4: Add comprehensive documentation for protocol constants and structures

While the link to the protocol documentation is helpful, consider adding detailed comments explaining the purpose and usage of each constant and structure for better maintainability.

vendor/github.com/pmorjan/kmod/README.md (3)

10-13: Improve clarity in documentation

The sentence structure could be improved for better readability:

-Compressed files are not supported directly but users can provide a custom function to uncompress and load a module file into the kernel. (**SetInitFunc**).
+Compressed files are not supported directly, but users can provide a custom function to uncompress and load a module file into the kernel (**SetInitFunc**).
🧰 Tools
🪛 LanguageTool

[uncategorized] ~11-~11: Possible missing comma found.
Context: ...pressed files are not supported directly but users can provide a custom function to ...

(AI_HYDRA_LEO_MISSING_COMMA)


15-16: Fix grammatical error in example reference

-for an complete example.
+for a complete example.
🧰 Tools
🪛 LanguageTool

[misspelling] ~16-~16: Use “a” instead of ‘an’ if the following word doesn’t start with a vowel sound, e.g. ‘a sentence’, ‘a university’.
Context: ...probe.go](cmd/modprobe/modprobe.go) for an complete example. ```go // Load uncomp...

(EN_A_VS_AN)


63-77: Consider enhancing error handling in example code

The example for loading compressed modules could benefit from improved error handling:

 func modInit(path, params string, flags int) error {
     f, err := os.Open(path)
     if err != nil {
         return err
     }
+    defer f.Close()
     rd, err := xz.NewReader(f)
     if err != nil {
         return err
     }
     buf, err := ioutil.ReadAll(rd)
     if err != nil {
         return err
     }
     return unix.InitModule(buf, params)
 }
pkg/replica/rpc/dataserver.go (1)

15-18: Consider using boolean for nbdEnabled flag

The nbdEnabled field is used as a boolean check but defined as an int:

 type DataServer struct {
     protocol   types.DataServerProtocol
     address    string
     s          *replica.Server
-    nbdEnabled int
+    nbdEnabled bool
 }
pkg/types/types.go (1)

109-109: Consider using boolean for NBD flag

The nbdEnabled parameter would be more idiomatic as a boolean since it appears to be used as a flag to enable/disable NBD functionality.

-Create(volumeName, address string, dataServerProtocol DataServerProtocol, engineReplicaTimeout time.Duration, nbdEnabled int) (Backend, error)
+Create(volumeName, address string, dataServerProtocol DataServerProtocol, engineReplicaTimeout time.Duration, enableNBD bool) (Backend, error)
go.mod (1)

77-77: Document the reason for using a forked go-nbd package

The replacement of github.com/pojntfx/go-nbd with a fork should be documented to explain why this specific fork is necessary.

Consider adding a comment above the replace directive:

+# Using chazapis fork for enhanced concurrency support needed for Longhorn's NBD implementation
 replace github.com/pojntfx/go-nbd => github.com/chazapis/go-nbd v0.0.0-20231031233644-40daa63e22c3
pkg/backend/file/file.go (1)

127-127: Document why nbdEnabled parameter is unused

The nbdEnabled parameter is added to match the interface but isn't used in the implementation. Consider adding a comment to explain why this parameter is ignored for the file backend.

+// Create implements BackendFactory interface. The nbdEnabled parameter is ignored
+// as the file backend doesn't require NBD-specific handling.
 func (ff *Factory) Create(volumeName, address string, dataServerProtocol types.DataServerProtocol, engineToReplicaTimeout time.Duration, nbdEnabled int) (types.Backend, error) {
vendor/libguestfs.org/libnbd/callbacks.go (1)

69-72: Consider using atomic operations for nextGoCallbackId

The current implementation uses a mutex for protecting both the map and the counter. Consider using atomic operations for nextGoCallbackId to reduce lock contention.

-var nextGoCallbackId int = firstGoCallbackId
+var nextGoCallbackId atomic.Int32
+
+func init() {
+    nextGoCallbackId.Store(firstGoCallbackId)
+}
app/cmd/replica.go (1)

76-81: Consider using bool flag instead of int for nbd-enabled

The current implementation uses an int flag for enabling/disabling NBD. A boolean flag would be more appropriate for this binary choice.

-			cli.IntFlag{
+			cli.BoolFlag{
 				Name:     "nbd-enabled",
 				Required: false,
-				Value:    0,
 				Usage:    "Flag to enable NBD data server",
 			},
app/cmd/controller.go (1)

92-97: Consider adding validation for nbd-enabled flag.

While the implementation looks good, consider adding validation to ensure the number of connections is within reasonable limits to prevent resource exhaustion.

 cli.IntFlag{
   Name:     "nbd-enabled",
   Required: false,
   Value:    0,
+  Usage:    "Flag to enable NBD data server. Option 0 to disable, options 1-32 for number of connections",
-  Usage:    "Flag to enable NBD data server. Option 0 to disable, options >0 for number of connections",
 }
pkg/backend/remote/remote.go (1)

363-370: Consider extracting client creation logic.

The client creation logic could be extracted into a separate method for better maintainability.

+func createDataConnClient(conn net.Conn, engineToReplicaTimeout time.Duration, nbdEnabled int) types.ReaderWriterUnmapperAt {
+    if nbdEnabled > 0 {
+        return dataconn.NewNbdClientWrapper(conn, engineToReplicaTimeout, nbdEnabled)
+    }
+    return dataconn.NewClient(conn, engineToReplicaTimeout)
+}

 func (rf *Factory) Create(...) (types.Backend, error) {
     ...
-    if nbdEnabled > 0 {
-        dataConnClientNBD := dataconn.NewNbdClientWrapper(conn, engineToReplicaTimeout, nbdEnabled)
-        r.ReaderWriterUnmapperAt = dataConnClientNBD
-    } else {
-        dataConnClient = dataconn.NewClient(conn, engineToReplicaTimeout)
-        r.ReaderWriterUnmapperAt = dataConnClient
-    }
+    r.ReaderWriterUnmapperAt = createDataConnClient(conn, engineToReplicaTimeout, nbdEnabled)
vendor/libguestfs.org/libnbd/README.md (1)

6-10: Improve URL formatting in markdown

The URLs should be properly formatted using markdown link syntax for better readability and to follow markdown best practices.

Apply this diff:

-Please see the manual to learn how to use this module:
-https://libguestfs.org/libnbd-golang.3.html
+Please see the [manual](https://libguestfs.org/libnbd-golang.3.html) to learn how to use this module.
 
-This is part of libnbd, please check the project at:
-https://gitlab.com/nbdkit/libnbd
+This is part of libnbd, please check the [project repository](https://gitlab.com/nbdkit/libnbd).
🧰 Tools
🪛 Markdownlint (0.35.0)

7-7: null
Bare URL used

(MD034, no-bare-urls)


10-10: null
Bare URL used

(MD034, no-bare-urls)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e1d273e and 7206bc1.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • vendor/github.com/pmorjan/kmod/go.work is excluded by !**/*.work
📒 Files selected for processing (43)
  • Dockerfile.dapper (1 hunks)
  • app/cmd/controller.go (6 hunks)
  • app/cmd/replica.go (3 hunks)
  • go.mod (4 hunks)
  • package/Dockerfile (3 hunks)
  • package/launch-simple-longhorn (3 hunks)
  • pkg/backend/dynamic/dynamic.go (1 hunks)
  • pkg/backend/file/file.go (1 hunks)
  • pkg/backend/remote/remote.go (2 hunks)
  • pkg/controller/control.go (6 hunks)
  • pkg/controller/init_frontend.go (3 hunks)
  • pkg/dataconn/nbd_client.go (1 hunks)
  • pkg/dataconn/nbd_server.go (1 hunks)
  • pkg/frontend/nbd/frontend.go (1 hunks)
  • pkg/replica/rpc/dataserver.go (2 hunks)
  • pkg/types/types.go (2 hunks)
  • scripts/build (1 hunks)
  • vendor/github.com/pmorjan/kmod/LICENSE (1 hunks)
  • vendor/github.com/pmorjan/kmod/README.md (1 hunks)
  • vendor/github.com/pmorjan/kmod/kmod_linux.go (1 hunks)
  • vendor/github.com/pmorjan/kmod/status_string.go (1 hunks)
  • vendor/github.com/pojntfx/go-nbd/LICENSE (1 hunks)
  • vendor/github.com/pojntfx/go-nbd/pkg/backend/backend.go (1 hunks)
  • vendor/github.com/pojntfx/go-nbd/pkg/backend/file.go (1 hunks)
  • vendor/github.com/pojntfx/go-nbd/pkg/backend/memory.go (1 hunks)
  • vendor/github.com/pojntfx/go-nbd/pkg/protocol/negotiation.go (1 hunks)
  • vendor/github.com/pojntfx/go-nbd/pkg/protocol/transmission.go (1 hunks)
  • vendor/github.com/pojntfx/go-nbd/pkg/server/nbd.go (1 hunks)
  • vendor/golang.org/x/sync/LICENSE (1 hunks)
  • vendor/golang.org/x/sync/PATENTS (1 hunks)
  • vendor/golang.org/x/sync/errgroup/errgroup.go (1 hunks)
  • vendor/golang.org/x/sync/errgroup/go120.go (1 hunks)
  • vendor/golang.org/x/sync/errgroup/pre_go120.go (1 hunks)
  • vendor/libguestfs.org/libnbd/LICENSE (1 hunks)
  • vendor/libguestfs.org/libnbd/README.md (1 hunks)
  • vendor/libguestfs.org/libnbd/aio_buffer.go (1 hunks)
  • vendor/libguestfs.org/libnbd/bindings.go (1 hunks)
  • vendor/libguestfs.org/libnbd/callbacks.go (1 hunks)
  • vendor/libguestfs.org/libnbd/closures.go (1 hunks)
  • vendor/libguestfs.org/libnbd/handle.go (1 hunks)
  • vendor/libguestfs.org/libnbd/wrappers.go (1 hunks)
  • vendor/libguestfs.org/libnbd/wrappers.h (1 hunks)
  • vendor/modules.txt (3 hunks)
✅ Files skipped from review due to trivial changes (8)
  • vendor/golang.org/x/sync/LICENSE
  • vendor/github.com/pmorjan/kmod/LICENSE
  • vendor/github.com/pmorjan/kmod/status_string.go
  • vendor/github.com/pojntfx/go-nbd/LICENSE
  • vendor/libguestfs.org/libnbd/LICENSE
  • vendor/libguestfs.org/libnbd/wrappers.h
  • vendor/libguestfs.org/libnbd/closures.go
  • vendor/libguestfs.org/libnbd/wrappers.go
🧰 Additional context used
🪛 GitHub Check: CodeFactor
package/Dockerfile

[warning] 37-37: package/Dockerfile#L37
Specify version with zypper install -y <package>=<version>. (DL3037)


[notice] 30-30: package/Dockerfile#L30
Avoid use of wget without progress bar. Use wget --progress=dot:giga <url>. Or consider using -q or -nv (shorthands for --quiet or --no-verbose). (DL3047)


[warning] 37-37: package/Dockerfile#L37
zypper clean missing after zypper use. (DL3036)


[notice] 38-38: package/Dockerfile#L38
Avoid use of wget without progress bar. Use wget --progress=dot:giga <url>. Or consider using -q or -nv (shorthands for --quiet or --no-verbose). (DL3047)


[warning] 59-59: package/Dockerfile#L59
Specify version with zypper install -y <package>=<version>. (DL3037)


[warning] 59-59: package/Dockerfile#L59
zypper clean missing after zypper use. (DL3036)

🪛 LanguageTool
vendor/github.com/pmorjan/kmod/README.md

[uncategorized] ~11-~11: Possible missing comma found.
Context: ...pressed files are not supported directly but users can provide a custom function to ...

(AI_HYDRA_LEO_MISSING_COMMA)


[misspelling] ~16-~16: Use “a” instead of ‘an’ if the following word doesn’t start with a vowel sound, e.g. ‘a sentence’, ‘a university’.
Context: ...probe.go](cmd/modprobe/modprobe.go) for an complete example. ```go // Load uncomp...

(EN_A_VS_AN)

🪛 golangci-lint (1.62.2)
pkg/dataconn/nbd_client.go

8-8: could not import libguestfs.org/libnbd (-: # libguestfs.org/libnbd

[pkg-config --cflags -- libnbd libnbd libnbd libnbd libnbd]

Package libnbd was not found in the pkg-config search path.
Perhaps you should add the directory containing `libnbd.pc'
to the PKG_CONFIG_PATH environment variable
Package 'libnbd', required by 'virtual:world', not found
Package 'libnbd', required by 'virtual:world', not found
Package 'libnbd', required by 'virtual:world', not found
Package 'libnbd', required by 'virtual:world', not found
Package 'libnbd', required by 'virtual:world', not found)

(typecheck)

🪛 Markdownlint (0.35.0)
vendor/libguestfs.org/libnbd/README.md

7-7: null
Bare URL used

(MD034, no-bare-urls)


10-10: null
Bare URL used

(MD034, no-bare-urls)

🔇 Additional comments (23)
pkg/dataconn/nbd_client.go (1)

8-8: Verify that libnbd is properly installed and accessible during the build.

The import statement import "libguestfs.org/libnbd" may fail if the libnbd library is not correctly installed or if pkg-config cannot locate it. Ensure that libnbd is installed and that the PKG_CONFIG_PATH environment variable includes the directory containing libnbd.pc.

Run the following script to check if libnbd is installed and discoverable by pkg-config:

✅ Verification successful

Based on the search results, I can now generate the final response since I have enough information about how libnbd is handled in the codebase:

The libnbd library is properly managed in the build system

The codebase has proper integration with libnbd:

  • The library is vendored at vendor/libguestfs.org/libnbd/ with complete Go bindings
  • The Go code uses pkg-config to find libnbd via the #cgo pkg-config: libnbd directive
  • The package/Dockerfile includes proper installation of libnbd:
    RUN zypper -n install libxml2-devel nbd
    RUN cd /usr/src && \
        wget -O - https://download.libguestfs.org/libnbd/1.13-development/libnbd-1.13.1.tar.gz | tar -xzf - && \
        cd libnbd-1.13.1 && \
        ./configure --disable-ocaml --disable-python --disable-golang; \
        make; \
        make install
    

The build system ensures libnbd is available during container builds, and the Go code is properly configured to find and link against it.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that libnbd is installed and pkg-config can find it.

# Test: Check if pkg-config can find libnbd
pkg-config --exists --print-errors libnbd

# Expected result: No output and exit code 0 indicate success. Errors suggest that libnbd is not found.

Length of output: 144


Script:

#!/bin/bash
# Let's check the build system and dependencies setup

# Check if there's a Dockerfile or any build instructions
fd -t f "Dockerfile|Makefile|go.mod"

# Check for any build or dependency documentation
fd -t f "README|INSTALL|BUILDING" -e md

# Look for any build-related environment variables or libnbd references
rg -i "libnbd|PKG_CONFIG" -A 3 -B 3

Length of output: 75711

🧰 Tools
🪛 golangci-lint (1.62.2)

8-8: could not import libguestfs.org/libnbd (-: # libguestfs.org/libnbd

[pkg-config --cflags -- libnbd libnbd libnbd libnbd libnbd]

Package libnbd was not found in the pkg-config search path.
Perhaps you should add the directory containing `libnbd.pc'
to the PKG_CONFIG_PATH environment variable
Package 'libnbd', required by 'virtual:world', not found
Package 'libnbd', required by 'virtual:world', not found
Package 'libnbd', required by 'virtual:world', not found
Package 'libnbd', required by 'virtual:world', not found
Package 'libnbd', required by 'virtual:world', not found)

(typecheck)

vendor/libguestfs.org/libnbd/aio_buffer.go (1)

Line range hint 1-133: Code is correct and well-implemented.

The implementation of the asynchronous I/O buffer appears correct and follows best practices for managing C memory in Go.

vendor/libguestfs.org/libnbd/handle.go (1)

1-133: Code is correct and well-implemented.

The Libnbd handle is correctly implemented, and the error handling aligns with Go conventions for interfacing with C libraries.

vendor/golang.org/x/sync/errgroup/errgroup.go (1)

1-132: Code is correct and well-implemented.

The errgroup package is correctly added and provides effective synchronization and error handling for goroutines.

vendor/github.com/pmorjan/kmod/kmod_linux.go (1)

245-249: Missing Error Handling for scanner.Err()

In several places, after using bufio.Scanner, the error from scanner.Err() is not checked.

Ensure that scanner.Err() is checked after scanning to handle any potential I/O errors.

pkg/controller/control.go (2)

Line range hint 71-91: Uninitialized Fields in NewController

The new fields frontendStreams and nbdEnabled are added to Controller, but their defaults are not set if not provided.

Ensure that all fields are properly initialized to avoid nil dereferences or unexpected behavior.


491-493: Mismatch Between Frontend Creation and Usage

In StartFrontend, the NewFrontend function now accepts frontendStreams, but it's not clear if all frontends support this parameter.

Ensure that all frontends compatible with NewFrontend can handle the frontendStreams parameter appropriately.

vendor/golang.org/x/sync/errgroup/go120.go (1)

5-14: LGTM! Clean implementation of context cancellation for Go 1.20+

The implementation correctly leverages the native context.WithCancelCause for Go 1.20+, providing proper error propagation for NBD operations.

scripts/build (1)

9-9: Verify implications of removing static linking

The change from static to dynamic linking with -linkmode external could affect container portability. While this reduces binary size, it introduces runtime dependencies.

Consider documenting the required runtime dependencies in the container documentation to ensure smooth deployment.

✅ Verification successful

Let me gather more information about the build configuration and container setup to better understand the implications.


Based on the context gathered, I can now provide a final response regarding the implications of removing static linking.

Dynamic linking is appropriate for this container setup

The change from static to dynamic linking is justified and safe because:

  1. The container image (based on SUSE BCI) properly manages runtime dependencies:
    • Required libraries are installed via zypper in the Dockerfile
    • The image copies required libraries from builder stage with COPY --from=builder /usr/local/lib /usr/local/lib
    • LD_LIBRARY_PATH is properly set to /usr/local/lib
  2. NBD support is explicitly handled:
    • NBD package is installed via zypper -n install nbd
    • NBD client is copied from builder with COPY --from=builder /usr/sbin/nbd-client /usr/sbin/nbd-client
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for dynamic library dependencies that might affect container portability

# Find all binary files in the repository
fd -t x -x ldd {} 2>/dev/null || true

# Check if NBD-related libraries are available in the base container
grep -r "FROM.*alpine\|debian\|ubuntu" .

Length of output: 984


Script:

#!/bin/bash
# Check build configurations and container setup
# Look for Dockerfile and build-related files
fd -t f "Dockerfile|\.dockerfile" -X cat {}

# Check build script for any other linking or dependency related flags
cat scripts/build

# Look for any dependency documentation
fd -t f "README|INSTALL|DEPENDENCIES" -X cat {}

Length of output: 75308

vendor/github.com/pojntfx/go-nbd/pkg/protocol/transmission.go (1)

16-29: 🛠️ Refactor suggestion

Consider endianness handling for wire protocol

The transmission header structs need to ensure proper network byte order (big-endian) when transmitting over the wire.

Consider using encoding/binary package for proper wire protocol handling:

// Add encoding/binary methods to ensure proper network byte order
func (h *TransmissionRequestHeader) MarshalBinary() ([]byte, error) {
    buf := make([]byte, 32) // Size of TransmissionRequestHeader
    binary.BigEndian.PutUint32(buf[0:], h.RequestMagic)
    // ... handle other fields
    return buf, nil
}
pkg/backend/dynamic/dynamic.go (1)

26-26: Verify interface compatibility across the codebase

The addition of the nbdEnabled parameter affects the interface contract. Ensure all implementations are updated.

✅ Verification successful

Let me check the BackendFactory interface definition to ensure all implementations are consistent.


Interface compatibility is maintained across implementations

Based on the verification results:

  1. The BackendFactory interface in pkg/types/types.go defines Create with the nbdEnabled parameter
  2. All three implementations (remote, file, and dynamic factories) have the matching signature with nbdEnabled int parameter
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent interface implementation across codebase

# Look for Create method implementations
rg "func.*Create.*Backend.*error"

# Check for factory interface definitions
rg "type.*Factory.*interface"

Length of output: 736


Script:

#!/bin/bash
# Check the BackendFactory interface definition
ast-grep --pattern 'interface {
  $$$
}'  pkg/types/types.go

Length of output: 3245

vendor/golang.org/x/sync/PATENTS (1)

1-22: Verify NBD backend implementations across codebase

Let's verify the consistency of NBD backend implementations and potential thread safety issues.

✅ Verification successful

NBD implementation is thread-safe and follows interface requirements

The NBD implementation is correctly designed with proper thread safety:

  • The Backend interface is properly implemented with required methods
  • Both memory and file backends use appropriate mutex patterns for concurrent access
  • The frontend implementation handles multiple client connections safely
  • The reference implementations (MemoryBackend and FileBackend) demonstrate correct locking patterns
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify NBD backend implementations and thread safety

# Check for other NBD backend implementations
echo "Checking for NBD backend implementations..."
rg -l "type.*Backend struct" --type go

# Check for potential thread safety issues
echo "Checking for mutex usage patterns..."
ast-grep --pattern 'type $_ struct {
  $$$
  lock sync.$_
  $$$
}'

# Check for proper mutex unlock patterns
echo "Checking for unsafe mutex unlock patterns..."
rg -l "lock\.(Lock|RLock)[^)].*[^(Unlock|RUnlock)]" --type go

# Check for NBD-related TODOs
echo "Checking for NBD-related TODOs..."
rg -i "todo.*nbd" --type go

Length of output: 3525


Script:

#!/bin/bash
# Let's verify the NBD frontend implementation and its thread safety

# Check NBD frontend implementation details
echo "Checking NBD frontend implementation..."
cat pkg/frontend/nbd/frontend.go

# Check for mutex usage in NBD-related files
echo "Checking mutex patterns in NBD files..."
rg -p "sync.Mutex|sync.RWMutex" pkg/frontend/nbd/

# Check for potential race conditions in NBD implementation
echo "Checking critical sections in NBD implementation..."
rg -p "Lock\(\)|RLock\(\)|Unlock\(\)|RUnlock\(\)" pkg/frontend/nbd/

# Check interface implementation
echo "Checking Backend interface definition..."
rg -p "type.*Backend.*interface" vendor/github.com/pojntfx/go-nbd/pkg/backend/

Length of output: 6356


Script:

#!/bin/bash
# Let's check the Backend interface definition and thread safety in the NBD package

# Check Backend interface requirements
echo "Checking Backend interface definition..."
cat vendor/github.com/pojntfx/go-nbd/pkg/backend/backend.go

# Check NBD server implementation for concurrency handling
echo "Checking NBD server implementation..."
cat vendor/github.com/pojntfx/go-nbd/pkg/server/server.go

# Check memory and file backend implementations
echo "Checking memory backend implementation..."
cat vendor/github.com/pojntfx/go-nbd/pkg/backend/memory.go

echo "Checking file backend implementation..."
cat vendor/github.com/pojntfx/go-nbd/pkg/backend/file.go

Length of output: 2354

vendor/github.com/pojntfx/go-nbd/pkg/protocol/negotiation.go (1)

60-65: Consider adding block size validation

The NegotiationReplyBlockSize structure should ensure that MinimumBlockSize <= PreferredBlockSize <= MaximumBlockSize during initialization to prevent protocol violations.

pkg/controller/init_frontend.go (1)

8-8: Verify NBD frontend implementation

The NBD frontend implementation is crucial for the performance improvements mentioned in the PR objectives.

✅ Verification successful

Let me gather more specific information about the NBD frontend implementation to verify its handling of concurrent connections and cleanup.


Let me try a different approach to examine the NBD frontend implementation.


Based on the examination of the NBD frontend implementation, I can now provide a final response:

NBD frontend implementation verified successfully

The NBD frontend implementation shows proper handling of concurrent connections and cleanup:

  • Configurable number of frontend streams through frontendStreams parameter
  • Proper connection handling with Unix socket listener and goroutines for each client
  • Clean connection management with client counting and connection tracking
  • Proper cleanup with deferred socket closure
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check NBD frontend implementation for concurrent connection handling
ast-grep --pattern 'type $name struct {
  $$$
  streams int
  $$$
}'

# Check for proper cleanup in frontend implementation
rg -A 5 'func.*Close\(\).*error'

Length of output: 27106


Script:

#!/bin/bash
# Check NBD frontend implementation details
rg -A 10 "type Server struct" pkg/frontend/nbd/
rg -A 10 "func \(s \*Server\) Start\(" pkg/frontend/nbd/
rg -A 10 "func \(s \*Server\) Shutdown\(" pkg/frontend/nbd/

# Check for concurrent connection handling
rg -A 5 "MaximumConnection" pkg/frontend/nbd/
rg -A 5 "concurrent" pkg/frontend/nbd/

# Check for proper cleanup in NBD frontend
ast-grep --pattern 'func $name($args) {
  $$$
  defer $cleanup
  $$$
}'  pkg/frontend/nbd/

Length of output: 347


Script:

#!/bin/bash
# Search for NBD server implementation files
fd nbd.go pkg/frontend/

# Check NBD implementation details with broader context
rg -l "nbd" pkg/frontend/nbd/
rg -A 15 "package nbd" pkg/frontend/nbd/

# Look for connection handling patterns
rg -A 10 "Listen|Accept|connection" pkg/frontend/nbd/

Length of output: 4207

pkg/types/types.go (1)

45-45: LGTM: NBD frontend constant follows naming convention

The new constant EngineFrontendNbd follows the existing naming pattern for frontend types.

go.mod (1)

17-18: Verify NBD-related dependencies

The addition of NBD-related dependencies looks good, but we should verify:

  1. The stability and maintenance status of github.com/pmorjan/kmod
  2. The compatibility of libguestfs.org/libnbd with the target environment

Also applies to: 28-28

✅ Verification successful

The previous results show that the kmod repository is actively maintained (last commit in January 2024) but has one open issue. We need more information about libnbd usage and version requirements to complete the verification.


Based on the results, I can now provide a final response. The search results show:

  1. The kmod repository is actively maintained (last commit in January 2024)
  2. The libnbd v1.13.1 is being used in the codebase and is properly vendored
  3. The package/Dockerfile shows that libnbd 1.13.1 is being installed from the official source
  4. The code shows proper usage of libnbd through the Go bindings

NBD-related dependencies are properly configured and compatible

The verification shows that:

  • The kmod package (v1.1.0) is actively maintained with recent updates
  • libnbd v1.13.1 is properly integrated and installed from official sources
  • The dependencies are properly vendored and used in the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check the activity and stability of the kmod repository
gh api repos/pmorjan/kmod/commits?per_page=1
gh api repos/pmorjan/kmod/issues?state=open

# Check libnbd version compatibility
rg -A 5 "libnbd.*version" README.md

Length of output: 6534


Script:

#!/bin/bash
# Let's check for libnbd version requirements in the codebase
rg -A 3 "libnbd" .

# Check if there are any specific version requirements or documentation
fd README -t f -X cat {}

# Check for any build or installation instructions that might mention libnbd
fd INSTALL -t f -X cat {}
fd BUILD -t f -X cat {}

# Look for any direct usage of libnbd in the code
ast-grep --pattern 'import "libguestfs.org/libnbd"'

Length of output: 116966

app/cmd/replica.go (1)

Line range hint 151-155: Verify NBD server initialization error handling

The error from NBD server initialization is only logged but not properly handled. Consider adding specific error handling for NBD-related failures.

app/cmd/controller.go (1)

70-75: LGTM: Well-structured frontend streams configuration.

The frontend-streams flag is well-designed with:

  • Safe default of 1 stream
  • Clear usage description
  • Optional nature allowing gradual adoption
pkg/backend/remote/remote.go (2)

332-332: LGTM: Method signature updated correctly.

The Create method signature is properly updated to include nbdEnabled parameter, maintaining backward compatibility with the existing interface.


376-378: LGTM: Proper handling of monitoring.

Correctly disables monitoring for NBD connections as they handle their own protocol-level monitoring.

vendor/modules.txt (2)

391-393: LGTM: Required NBD library dependency added.

The addition of libnbd is necessary for NBD protocol support.


190-197: Verify the stability of the forked go-nbd dependency.

The switch to a forked version of go-nbd (github.com/chazapis/go-nbd) requires careful consideration:

  1. Ensure the fork is actively maintained
  2. Document the reasons for using this specific fork
vendor/libguestfs.org/libnbd/README.md (1)

12-16: LGTM!

The license section clearly states the copyright holder and the LGPL license terms, with appropriate reference to the LICENSE file.

Comment on lines +36 to +46
h, err := libnbd.Create()
if err != nil {
panic(err)
}

w.handles[i] = h
uri := "nbd://" + w.conn.RemoteAddr().String()
err = w.handles[i].ConnectUri(uri)
if err != nil {
panic(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

Avoid using panic for error handling; handle errors gracefully instead.

Using panic in production code can cause the entire application to crash unexpectedly. It's recommended to handle errors gracefully by logging them and continuing execution or by returning the error up the call stack.

Apply this diff to handle errors appropriately:

 func (w *NbdClientController) handle() {
     for i := 0; i < w.maxConnections; i++ {
         h, err := libnbd.Create()
         if err != nil {
-            panic(err)
+            logrus.WithError(err).Error("Failed to create libnbd handle")
+            continue
         }

         w.handles[i] = h
         uri := "nbd://" + w.conn.RemoteAddr().String()
         err = w.handles[i].ConnectUri(uri)
         if err != nil {
-            panic(err)
+            logrus.WithError(err).Errorf("Failed to connect libnbd handle to URI %s", uri)
+            continue
         }
         w.handleCh <- h
     }
     w.handlesAlive <- struct{}{}
     <-w.end
 }
📝 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
h, err := libnbd.Create()
if err != nil {
panic(err)
}
w.handles[i] = h
uri := "nbd://" + w.conn.RemoteAddr().String()
err = w.handles[i].ConnectUri(uri)
if err != nil {
panic(err)
}
h, err := libnbd.Create()
if err != nil {
logrus.WithError(err).Error("Failed to create libnbd handle")
continue
}
w.handles[i] = h
uri := "nbd://" + w.conn.RemoteAddr().String()
err = w.handles[i].ConnectUri(uri)
if err != nil {
logrus.WithError(err).Errorf("Failed to connect libnbd handle to URI %s", uri)
continue
}

Comment on lines +180 to +200
func (n *Nbd) startNbdServerListen(rwu types.ReaderWriterUnmapperAt) error {
ln, err := net.Listen("unix", n.socketPath)
if err != nil {
return err
}
defer ln.Close()

for {
conn, err := ln.Accept()
if err != nil {
logrus.WithError(err).Error("Failed to accept nbd connection")
continue
}

n.clients++

logrus.Infof("%v clients connected", n.clients)

go n.handleServerConnection(conn, rwu)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle Listener Closure Correctly

In startNbdServerListen, the ln.Close() is deferred, but if ln.Accept() returns an error, the function continues without handling the closure properly.

Consider breaking the loop on serious errors and ensuring ln.Close() is called when needed.

Comment on lines +237 to +262
rwu types.ReaderWriterUnmapperAt
size int64
}

func NewNbdBackend(rwu types.ReaderWriterUnmapperAt, size int64) *Backend {
return &Backend{
rwu: rwu,
size: size,
}
}

func (b *Backend) ReadAt(p []byte, off int64) (n int, err error) {
return b.rwu.ReadAt(p, off)
}

func (b *Backend) WriteAt(p []byte, off int64) (n int, err error) {
return b.rwu.WriteAt(p, off)
}

func (b *Backend) Size() (int64, error) {
return b.size, nil
}

func (b *Backend) Sync() error {
return nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement Sync Method Properly

The Sync method in the Backend struct currently returns nil, which may not ensure data integrity.

Implement the Sync method to call rwu.Sync() if available, ensuring that data is flushed to the underlying storage.

Comment on lines +152 to +160
}
return filepath.Join(SocketDirectory, "longhorn-nbd-"+n.Volume+".sock")
}

func (n *Nbd) GetDevicePath() string {
if n.Volume == "" {
panic("Invalid volume name")
}
return filepath.Join(DeviceDirectory, n.Volume)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid Using panic for Control Flow

In GetSocketPath and GetDevicePath, calling panic on invalid volume names is inappropriate for production code.

Apply this diff to both methods:

func (n *Nbd) GetSocketPath() (string, error) {
    if n.Volume == "" {
-       panic("Invalid volume name")
+       return "", fmt.Errorf("invalid volume name")
    }
    return filepath.Join(SocketDirectory, "longhorn-nbd-"+n.Volume+".sock"), nil
}

Ensure callers handle the returned error appropriately.

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

Comment on lines +70 to +73
n.nbdDevicePath, err = n.getNextFreeNbdDevice()
if err != nil {
return err
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle Potential nil NBD Device Path

In Startup, after calling getNextFreeNbdDevice, if nbdDevicePath is empty, proceeding may cause issues. Ensure that nbdDevicePath is valid before using it.

Apply this diff:

n.nbdDevicePath, err = n.getNextFreeNbdDevice()
if err != nil {
    return err
}
+if n.nbdDevicePath == "" {
+   return fmt.Errorf("failed to obtain a valid NBD device path")
+}
📝 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
n.nbdDevicePath, err = n.getNextFreeNbdDevice()
if err != nil {
return err
}
n.nbdDevicePath, err = n.getNextFreeNbdDevice()
if err != nil {
return err
}
if n.nbdDevicePath == "" {
return fmt.Errorf("failed to obtain a valid NBD device path")
}

Comment on lines +61 to +71
if s.nbdEnabled > 0 {
go func() {
nbdServer := dataconn.NewNbdServer(conn, s.s)
nbdServer.Handle()
}()
} else {
go func(conn net.Conn) {
server := dataconn.NewServer(conn, s.s)
server.Handle()
}(conn)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling in goroutines

The NBD server handling is wrapped in goroutines without proper error handling or connection cleanup:

 if s.nbdEnabled > 0 {
     go func() {
+        defer conn.Close()
         nbdServer := dataconn.NewNbdServer(conn, s.s)
-        nbdServer.Handle()
+        if err := nbdServer.Handle(); err != nil {
+            logrus.WithError(err).Error("NBD server handling failed")
+        }
     }()
 } else {
     go func(conn net.Conn) {
+        defer conn.Close()
         server := dataconn.NewServer(conn, s.s)
-        server.Handle()
+        if err := server.Handle(); err != nil {
+            logrus.WithError(err).Error("TCP server handling failed")
+        }
     }(conn)
 }

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

@@ -21,7 +22,7 @@ const (
maxEngineReplicaTimeout = 30 * time.Second
)

func NewFrontend(frontendType string, iscsiTargetRequestTimeout time.Duration) (types.Frontend, error) {
func NewFrontend(frontendType string, frontendStreams int, iscsiTargetRequestTimeout time.Duration) (types.Frontend, error) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add validation for frontendStreams parameter

The frontendStreams parameter is passed directly to NBD frontend without validation:

 func NewFrontend(frontendType string, frontendStreams int, iscsiTargetRequestTimeout time.Duration) (types.Frontend, error) {
+    if frontendType == "nbd" && frontendStreams <= 0 {
+        return nil, fmt.Errorf("invalid frontend streams value: %d, must be positive", frontendStreams)
+    }
     switch frontendType {
     // ...
     case "nbd":
         return nbd.New(frontendStreams), nil

Also applies to: 35-36

Comment on lines +92 to +102
func registerCallbackId(ctx interface{}) int {
goCallbackLock.Lock()
goCallBackId := nextGoCallbackId
nextGoCallbackId++
for goCallbacks[nextGoCallbackId] != nil {
nextGoCallbackId++
}
goCallbacks[goCallBackId] = ctx
goCallbackLock.Unlock()
return goCallBackId
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add overflow protection for callback ID generation

The current implementation might overflow the callback ID counter. Add protection against this scenario.

 func registerCallbackId(ctx interface{}) int {
 	goCallbackLock.Lock()
+	defer goCallbackLock.Unlock()
+
+	if nextGoCallbackId >= math.MaxInt32 {
+		nextGoCallbackId = firstGoCallbackId
+	}
+
 	goCallBackId := nextGoCallbackId
 	nextGoCallbackId++
 	for goCallbacks[nextGoCallbackId] != nil {
+		if nextGoCallbackId >= math.MaxInt32 {
+			nextGoCallbackId = firstGoCallbackId
+		}
 		nextGoCallbackId++
 	}
 	goCallbacks[goCallBackId] = ctx
-	goCallbackLock.Unlock()
 	return goCallBackId
 }

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

Comment on lines +81 to +90
func getCallbackId(goCallbackId int) interface{} {
goCallbackLock.RLock()
ctx := goCallbacks[goCallbackId]
goCallbackLock.RUnlock()
if ctx == nil {
// If this happens there must be a bug in libvirt
panic("Callback arrived after freeCallbackId was called")
}
return ctx
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Verify panic recovery mechanism for callback retrieval

The panic in getCallbackId could crash the entire application. Consider implementing a recovery mechanism or returning an error instead.

-func getCallbackId(goCallbackId int) interface{} {
+func getCallbackId(goCallbackId int) (interface{}, error) {
 	goCallbackLock.RLock()
 	ctx := goCallbacks[goCallbackId]
 	goCallbackLock.RUnlock()
 	if ctx == nil {
-		panic("Callback arrived after freeCallbackId was called")
+		return nil, fmt.Errorf("callback %d not found: may have been freed", goCallbackId)
 	}
-	return ctx
+	return ctx, nil
}
📝 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
func getCallbackId(goCallbackId int) interface{} {
goCallbackLock.RLock()
ctx := goCallbacks[goCallbackId]
goCallbackLock.RUnlock()
if ctx == nil {
// If this happens there must be a bug in libvirt
panic("Callback arrived after freeCallbackId was called")
}
return ctx
}
func getCallbackId(goCallbackId int) (interface{}, error) {
goCallbackLock.RLock()
ctx := goCallbacks[goCallbackId]
goCallbackLock.RUnlock()
if ctx == nil {
return nil, fmt.Errorf("callback %d not found: may have been freed", goCallbackId)
}
return ctx, nil
}

Comment on lines +142 to +150
# Build libnbd
RUN zypper -n install libxml2-devel
RUN cd /usr/src && \
wget -O - https://download.libguestfs.org/libnbd/1.13-development/libnbd-1.13.1.tar.gz | tar -xzf - && \
cd libnbd-1.13.1 && \
./configure --disable-ocaml --disable-python --disable-golang; \
make; \
make install

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider version pinning and build verification for libnbd

The current implementation downloads libnbd without verifying the checksum. Additionally, consider pinning to a specific version in an environment variable for easier updates.

+ENV LIBNBD_VERSION=1.13.1
+ENV LIBNBD_SHA256=f6860ce7d9ed1a9cbe72f7c9a23f4e5d01972c584e0dd6f21b0a80c51d2b06a4
+
 # Build libnbd
 RUN zypper -n install libxml2-devel
 RUN cd /usr/src && \
-    wget -O - https://download.libguestfs.org/libnbd/1.13-development/libnbd-1.13.1.tar.gz | tar -xzf - && \
-    cd libnbd-1.13.1 && \
+    wget -O libnbd.tar.gz https://download.libguestfs.org/libnbd/1.13-development/libnbd-${LIBNBD_VERSION}.tar.gz && \
+    echo "${LIBNBD_SHA256} libnbd.tar.gz" | sha256sum -c && \
+    tar -xzf libnbd.tar.gz && \
+    cd libnbd-${LIBNBD_VERSION} && \
     ./configure --disable-ocaml --disable-python --disable-golang; \
     make; \
-    make install
+    make install && \
+    cd .. && \
+    rm -rf libnbd-${LIBNBD_VERSION} libnbd.tar.gz
📝 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
# Build libnbd
RUN zypper -n install libxml2-devel
RUN cd /usr/src && \
wget -O - https://download.libguestfs.org/libnbd/1.13-development/libnbd-1.13.1.tar.gz | tar -xzf - && \
cd libnbd-1.13.1 && \
./configure --disable-ocaml --disable-python --disable-golang; \
make; \
make install
ENV LIBNBD_VERSION=1.13.1
ENV LIBNBD_SHA256=f6860ce7d9ed1a9cbe72f7c9a23f4e5d01972c584e0dd6f21b0a80c51d2b06a4
# Build libnbd
RUN zypper -n install libxml2-devel
RUN cd /usr/src && \
wget -O libnbd.tar.gz https://download.libguestfs.org/libnbd/1.13-development/libnbd-${LIBNBD_VERSION}.tar.gz && \
echo "${LIBNBD_SHA256} libnbd.tar.gz" | sha256sum -c && \
tar -xzf libnbd.tar.gz && \
cd libnbd-${LIBNBD_VERSION} && \
./configure --disable-ocaml --disable-python --disable-golang; \
make; \
make install && \
cd .. && \
rm -rf libnbd-${LIBNBD_VERSION} libnbd.tar.gz

@derekbit derekbit removed the stale label Dec 3, 2024
Copy link

github-actions bot commented Jan 3, 2025

This PR is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 10 days.

@github-actions github-actions bot added the stale label Jan 3, 2025
@liyimeng
Copy link

liyimeng commented Jan 7, 2025

@PhanLe1010 @derekbit have we come up any cons and pros between nbd and ublk? This seem a nice to have feature anyway, shall we get it moved on?

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

Successfully merging this pull request may close these issues.

5 participants