
seblegall
Distributed Services with Go - Method log.Read() fails reading Records
Hi,
I’m currently reading your book. By doing so, I like to rewrite the code by myself. It helps me to deeply understand how it works.
However, I found something strange in the log package, concerning the Read() func. Let’s see that with a test example :
In the log_test.go
file, the original test is :
func testAppendRead(t *testing.T, log *log.Log) {
append := &api.Record{
Value: []byte("hello world"),
}
off, err := log.Append(append)
require.NoError(t, err)
require.Equal(t, uint64(0), off)
read, err := log.Read(off)
require.NoError(t, err)
require.Equal(t, append, read)
}
In this test, we write 1 record and read it.
Let’s write 4 records and read them:
func testAppendRead(t *testing.T, log *log.Log) {
for i := 0; i < 5; i++ {
append := &api.Record{
Value: []byte(fmt.Sprintf("Hello World %d !", i)),
}
off, err := log.Append(append)
require.NoError(t, err)
require.Equal(t, uint64(i), off)
read, err := log.Read(off)
require.NoError(t, err)
require.Equal(t, append, read)
}
}
Here is what I get by running go test -v log_test.go
:
=== RUN TestLog
=== RUN TestLog/append_and_read_a_record_succeeds
TestLog/append_and_read_a_record_succeeds: log_test.go:48:
Error Trace: log_test.go:48
log_test.go:32
Error: Received unexpected error:
offset out of range: 2
Test: TestLog/append_and_read_a_record_succeeds
I’m not sure why it fails, but I have understood that, in a log, segments are ordered from the oldest to the newest. It means the oldest will have a base offset of 0 (for example), the second will have a base offset of 2.
But then, there is this code in the Read method :
var s *segment
for _, segment := range l.segments {
//The first segment we read is the oldest one. That is to say, the one
//with a base offset equals to 0, right?
if segment.baseOffset <= off {
s = segment
break
}
}
if s == nil || s.nextOffset <= off {
return nil, fmt.Errorf("offset out of range: %d", off)
}
If we try to read offset 3, by reading the first (oldest) segment in the loop, we will have: segment.baseOffset = 0 <= 3
However, the Record with the offset 3 is in the segment which baseOffset is 2.
Did I miss something?
Most Liked

travisjeffery
Hey, yeah it should be implemented like this:
func (l *Log) Read(off uint64) (*api.Record, error) {
l.mu.RLock()
defer l.mu.RUnlock()
var s *segment
for _, segment := range l.segments {
if segment.baseOffset <= off && off < segment.nextOffset {
s = segment
break
}
}
// START: before
if s == nil || s.nextOffset <= off {
return nil, fmt.Errorf("offset out of range: %d", off)
}
// END: before
return s.Read(off)
}
I fixed a ton of issues that will be in the next beta, which should be out either late this week or next week.
Popular Prag Prog topics










Other popular topics










Latest in PragProg
Latest (all)
Categories:
Popular Portals
- /elixir
- /rust
- /wasm
- /ruby
- /erlang
- /phoenix
- /keyboards
- /js
- /rails
- /python
- /security
- /go
- /swift
- /vim
- /clojure
- /java
- /haskell
- /emacs
- /svelte
- /onivim
- /typescript
- /crystal
- /c-plus-plus
- /tailwind
- /kotlin
- /gleam
- /react
- /flutter
- /elm
- /ocaml
- /vscode
- /opensuse
- /ash
- /centos
- /php
- /deepseek
- /zig
- /scala
- /html
- /debian
- /nixos
- /lisp
- /agda
- /textmate
- /sublime-text
- /react-native
- /kubuntu
- /arch-linux
- /ubuntu
- /revery
- /manjaro
- /django
- /spring
- /diversity
- /nodejs
- /lua
- /slackware
- /julia
- /c
- /neovim