Skip to content

bug: (bindings/go): Reader does not return io.EOF at end of file, causing non-terminating loop. #6148

@shanipribadi

Description

@shanipribadi

Describe the bug

As per the doc in https://pkg.go.dev/github.com/apache/opendal/bindings/go#Reader.Read,
opendal Reader.Read does not return (0, io.EOF) error at end of file.

as per the io.Reader doc in https://pkg.go.dev/io#Reader

Implementations of Read are discouraged from returning a zero byte count with a nil error, except when len(p) == 0. Callers should treat a return of 0 and nil as indicating that nothing happened; in particular it does not indicate EOF.

what this means is that opendal.Read does not play well with other golang functions that expects io.EOF behaviour,
one particular example is when using io.Copy, it would just block indefinitely in a loop due to the above.

Steps to Reproduce

mkdir /tmp/bucket_src
mkdir /tmp/bucket_dst
head -c 10M /tmp/bucket_src/testfile
package main

import (
	"io"
	"log"

	"github.com/apache/opendal-go-services/fs"
	"github.com/apache/opendal/bindings/go"
)

func main() {
	srcOp, err := opendal.NewOperator(fs.Scheme, opendal.OperatorOptions{
		"root": "/tmp/bucket_src",
	})
	if err != nil {
		log.Fatalf("err: %v", err)
	}
	defer srcOp.Close()
	dstOp, err := opendal.NewOperator(fs.Scheme, opendal.OperatorOptions{
		"root": "/tmp/bucket_dst",
	})
	if err != nil {
		log.Fatalf("err: %v", err)
	}
	defer dstOp.Close()

	rdr, err := srcOp.Reader("testfile")
	if err != nil {
		log.Fatalf("err: %v", err)
	}
	defer rdr.Close()
	wr, err := dstOp.Writer("testfile")
	if err != nil {
		log.Fatalf("err: %v", err)
	}
	defer wr.Close()

	n, err := io.Copy(wr, rdr)
	if err != nil {
		log.Fatalf("err: %v", err)
	}
	log.Printf("written: %d bytes", n)
}

Expected Behavior

the program terminates once it finish reading the file.

Additional Context

a simple wrapper for the reader does work as a workaround. but it would be better if the behaviour is implemented by the opendal go binding itself.

type reader struct {
	r io.Reader
}

func (r *reader) Read(p []byte) (int, error) {
	n, err := r.r.Read(p)
	if n == 0 && err == nil {
		return n, io.EOF
	}
	return n, err
}

Are you willing to submit a PR to fix this bug?

  • Yes, I would like to submit a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions