github.com/cilium/cilium@v1.16.2/pkg/hubble/container/ring_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Hubble
     3  
     4  package container
     5  
     6  import (
     7  	"container/list"
     8  	"container/ring"
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"reflect"
    14  	"sync"
    15  	"testing"
    16  
    17  	"github.com/google/go-cmp/cmp"
    18  	"github.com/google/go-cmp/cmp/cmpopts"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  	"go.uber.org/goleak"
    22  	"google.golang.org/protobuf/types/known/timestamppb"
    23  
    24  	flowpb "github.com/cilium/cilium/api/v1/flow"
    25  	v1 "github.com/cilium/cilium/pkg/hubble/api/v1"
    26  )
    27  
    28  func BenchmarkRingWrite(b *testing.B) {
    29  	entry := &v1.Event{}
    30  	s := NewRing(capacity(b.N))
    31  	b.ReportAllocs()
    32  	b.ResetTimer()
    33  	for i := 0; i < b.N; i++ {
    34  		s.Write(entry)
    35  	}
    36  }
    37  
    38  func BenchmarkRingRead(b *testing.B) {
    39  	entry := &v1.Event{}
    40  	s := NewRing(capacity(b.N))
    41  	a := make([]*v1.Event, b.N)
    42  	b.ReportAllocs()
    43  	for i := 0; i < b.N; i++ {
    44  		s.Write(entry)
    45  	}
    46  	b.ResetTimer()
    47  	lastWriteIdx := s.LastWriteParallel()
    48  	for i := 0; i < b.N; i++ {
    49  		a[i], _ = s.read(lastWriteIdx)
    50  		lastWriteIdx--
    51  	}
    52  }
    53  
    54  func BenchmarkTimeLibListRead(b *testing.B) {
    55  	entry := &v1.Event{}
    56  	s := list.New()
    57  	a := make([]*v1.Event, b.N)
    58  	i := 0
    59  	b.ReportAllocs()
    60  	for i := 0; i < b.N; i++ {
    61  		s.PushFront(entry)
    62  	}
    63  	b.ResetTimer()
    64  	for e := s.Front(); e != nil; e = e.Next() {
    65  		a[i], _ = e.Value.(*v1.Event)
    66  	}
    67  }
    68  
    69  func BenchmarkTimeLibRingRead(b *testing.B) {
    70  	entry := &v1.Event{}
    71  	s := ring.New(b.N)
    72  	a := make([]*v1.Event, b.N)
    73  	i := 0
    74  	b.ReportAllocs()
    75  	b.ResetTimer()
    76  	for i := 0; i < b.N; i++ {
    77  		s.Value = entry
    78  		s.Next()
    79  	}
    80  	s.Do(func(e interface{}) {
    81  		a[i], _ = e.(*v1.Event)
    82  		i++
    83  	})
    84  }
    85  
    86  func TestNewCapacity(t *testing.T) {
    87  	// check all valid values according to the doc string
    88  	// ie: value of n MUST satisfy n=2^i -1 for i = [1, 16]
    89  	for i := 1; i <= 16; i++ {
    90  		n := (1 << i) - 1
    91  		t.Run(fmt.Sprintf("n=%d", n), func(t *testing.T) {
    92  			c, err := NewCapacity(n)
    93  			assert.NoError(t, err)
    94  			assert.Equal(t, capacity(n), c)
    95  		})
    96  	}
    97  	// validate CapacityN constants
    98  	capacityN := []capacity{
    99  		Capacity1,
   100  		Capacity3,
   101  		Capacity7,
   102  		Capacity15,
   103  		Capacity31,
   104  		Capacity63,
   105  		Capacity127,
   106  		Capacity255,
   107  		Capacity511,
   108  		Capacity1023,
   109  		Capacity2047,
   110  		Capacity4095,
   111  		Capacity8191,
   112  		Capacity16383,
   113  		Capacity32767,
   114  		Capacity65535,
   115  	}
   116  	for _, n := range capacityN {
   117  		t.Run(fmt.Sprintf("n=Capacity%d", n.AsInt()), func(t *testing.T) {
   118  			c, err := NewCapacity(n.AsInt())
   119  			assert.NoError(t, err)
   120  			assert.Equal(t, n, c)
   121  		})
   122  	}
   123  
   124  	// test invalid values
   125  	for _, n := range []int{-127, -10, 0, 2, 128, 131071} {
   126  		t.Run(fmt.Sprintf("n=%d", n), func(t *testing.T) {
   127  			c, err := NewCapacity(n)
   128  			assert.Nil(t, c)
   129  			assert.NotNil(t, err)
   130  		})
   131  	}
   132  }
   133  
   134  func TestNewRing(t *testing.T) {
   135  	for i := 1; i <= 16; i++ {
   136  		n := (1 << i) - 1
   137  		t.Run(fmt.Sprintf("n=%d", n), func(t *testing.T) {
   138  			r := NewRing(capacity(n))
   139  			require.NotNil(t, r)
   140  			assert.Equal(t, uint64(0), r.Len())
   141  			assert.Equal(t, uint64(n), r.Cap())
   142  			// fill half the buffer
   143  			for j := 0; j < n/2; j++ {
   144  				r.Write(&v1.Event{})
   145  			}
   146  			assert.Equal(t, uint64(n/2), r.Len())
   147  			assert.Equal(t, uint64(n), r.Cap())
   148  			// fill the buffer to max capacity
   149  			for j := 0; j <= n/2; j++ {
   150  				r.Write(&v1.Event{})
   151  			}
   152  			assert.Equal(t, uint64(n), r.Len())
   153  			assert.Equal(t, uint64(n), r.Cap())
   154  			// write more events
   155  			for j := 0; j < n; j++ {
   156  				r.Write(&v1.Event{})
   157  			}
   158  			assert.Equal(t, uint64(n), r.Len())
   159  			assert.Equal(t, uint64(n), r.Cap())
   160  		})
   161  	}
   162  }
   163  
   164  func TestRing_Read(t *testing.T) {
   165  	type fields struct {
   166  		mask     uint64
   167  		cycleExp uint8
   168  		data     []*v1.Event
   169  		write    uint64
   170  	}
   171  	type args struct {
   172  		read uint64
   173  	}
   174  	tests := []struct {
   175  		name    string
   176  		fields  fields
   177  		args    args
   178  		want    *v1.Event
   179  		wantErr error
   180  	}{
   181  		{
   182  			name: "normal read for the index 7",
   183  			fields: fields{
   184  				mask:     0x7,
   185  				cycleExp: 0x3, // 7+1=8=2^3
   186  				data: []*v1.Event{
   187  					0x0: {Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   188  					0x1: {Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   189  					0x2: {Timestamp: &timestamppb.Timestamp{Seconds: 2}},
   190  					0x3: {Timestamp: &timestamppb.Timestamp{Seconds: 3}},
   191  					0x4: {Timestamp: &timestamppb.Timestamp{Seconds: 4}},
   192  					0x5: {Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   193  					0x6: {Timestamp: &timestamppb.Timestamp{Seconds: 6}},
   194  					0x7: {Timestamp: &timestamppb.Timestamp{Seconds: 7}},
   195  				},
   196  				// next to be written: 0x9 (idx: 1), last written: 0x8 (idx: 0)
   197  				write: 0x9,
   198  			},
   199  			args: args{
   200  				read: 0x7,
   201  			},
   202  			want:    &v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 7}},
   203  			wantErr: nil,
   204  		},
   205  		{
   206  			name: "we can't read index 0 since we just wrote into it",
   207  			fields: fields{
   208  				mask:     0x7,
   209  				cycleExp: 0x3, // 7+1=8=2^3
   210  				data: []*v1.Event{
   211  					0x0: {Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   212  					0x1: {Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   213  					0x2: {Timestamp: &timestamppb.Timestamp{Seconds: 2}},
   214  					0x3: {Timestamp: &timestamppb.Timestamp{Seconds: 3}},
   215  					0x4: {Timestamp: &timestamppb.Timestamp{Seconds: 4}},
   216  					0x5: {Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   217  					0x6: {Timestamp: &timestamppb.Timestamp{Seconds: 6}},
   218  					0x7: {Timestamp: &timestamppb.Timestamp{Seconds: 7}},
   219  				},
   220  				// next to be written: 0x9 (idx: 2), last written: 0x8 (idx: 0)
   221  				write: 0x9,
   222  			},
   223  			args: args{
   224  				read: 0x0,
   225  			},
   226  			want: &v1.Event{
   227  				Event: &flowpb.LostEvent{
   228  					Source:        flowpb.LostEventSource_HUBBLE_RING_BUFFER,
   229  					NumEventsLost: 1,
   230  					Cpu:           nil,
   231  				},
   232  			},
   233  			wantErr: nil,
   234  		},
   235  		{
   236  			name: "we can't read index 0x7 since we are one writing cycle ahead",
   237  			fields: fields{
   238  				mask:     0x7,
   239  				cycleExp: 0x3, // 7+1=8=2^3
   240  				data: []*v1.Event{
   241  					0x0: {Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   242  					0x1: {Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   243  					0x2: {Timestamp: &timestamppb.Timestamp{Seconds: 2}},
   244  					0x3: {Timestamp: &timestamppb.Timestamp{Seconds: 3}},
   245  					0x4: {Timestamp: &timestamppb.Timestamp{Seconds: 4}},
   246  					0x5: {Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   247  					0x6: {Timestamp: &timestamppb.Timestamp{Seconds: 6}},
   248  					0x7: {Timestamp: &timestamppb.Timestamp{Seconds: 7}},
   249  				},
   250  				// next to be written: 0x10 (idx: 0), last written: 0x0f (idx: 7)
   251  				write: 0x10,
   252  			},
   253  			args: args{
   254  				// The next possible entry that we can read is 0x10-0x7-0x1 = 0x8 (idx: 0)
   255  				read: 0x7,
   256  			},
   257  			want: &v1.Event{
   258  				Event: &flowpb.LostEvent{
   259  					Source:        flowpb.LostEventSource_HUBBLE_RING_BUFFER,
   260  					NumEventsLost: 1,
   261  					Cpu:           nil,
   262  				},
   263  			},
   264  			wantErr: nil,
   265  		},
   266  		{
   267  			name: "we can read index 0x8 since it's the last entry that we can read in this cycle",
   268  			fields: fields{
   269  				mask:     0x7,
   270  				cycleExp: 0x3, // 7+1=8=2^3
   271  				data: []*v1.Event{
   272  					0x0: {Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   273  					0x1: {Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   274  					0x2: {Timestamp: &timestamppb.Timestamp{Seconds: 2}},
   275  					0x3: {Timestamp: &timestamppb.Timestamp{Seconds: 3}},
   276  					0x4: {Timestamp: &timestamppb.Timestamp{Seconds: 4}},
   277  					0x5: {Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   278  					0x6: {Timestamp: &timestamppb.Timestamp{Seconds: 6}},
   279  					0x7: {Timestamp: &timestamppb.Timestamp{Seconds: 7}},
   280  				},
   281  				// next to be written: 0x10 (idx: 0), last written: 0x0f (idx: 7)
   282  				write: 0x10,
   283  			},
   284  			args: args{
   285  				// The next possible entry that we can read is 0x10-0x7-0x1 = 0x8 (idx: 0)
   286  				read: 0x8,
   287  			},
   288  			want:    &v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   289  			wantErr: nil,
   290  		},
   291  		{
   292  			name: "we overflow write and we are trying to read the previous writes, that we can't",
   293  			fields: fields{
   294  				mask:     0x7,
   295  				cycleExp: 0x3, // 7+1=8=2^3
   296  				data: []*v1.Event{
   297  					0x0: {Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   298  					0x1: {Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   299  					0x2: {Timestamp: &timestamppb.Timestamp{Seconds: 2}},
   300  					0x3: {Timestamp: &timestamppb.Timestamp{Seconds: 3}},
   301  					0x4: {Timestamp: &timestamppb.Timestamp{Seconds: 4}},
   302  					0x5: {Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   303  					0x6: {Timestamp: &timestamppb.Timestamp{Seconds: 6}},
   304  					0x7: {Timestamp: &timestamppb.Timestamp{Seconds: 7}},
   305  				},
   306  				// next to be written: 0x0 (idx: 0), last written: 0xffffffffffffffff (idx: 7)
   307  				write: 0x0,
   308  			},
   309  			args: args{
   310  				// We can't read this index because we might be still writing into it
   311  				// next to be read: ^uint64(0) (idx: 7), last read: 0xfffffffffffffffe (idx: 6)
   312  				read: ^uint64(0),
   313  			},
   314  			want: &v1.Event{
   315  				Event: &flowpb.LostEvent{
   316  					Source:        flowpb.LostEventSource_HUBBLE_RING_BUFFER,
   317  					NumEventsLost: 1,
   318  					Cpu:           nil,
   319  				},
   320  			},
   321  			wantErr: nil,
   322  		},
   323  		{
   324  			name: "we overflow write and we are trying to read the previous writes, that we can",
   325  			fields: fields{
   326  				mask:     0x7,
   327  				cycleExp: 0x3, // 7+1=8=2^3
   328  				data: []*v1.Event{
   329  					0x0: {Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   330  					0x1: {Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   331  					0x2: {Timestamp: &timestamppb.Timestamp{Seconds: 2}},
   332  					0x3: {Timestamp: &timestamppb.Timestamp{Seconds: 3}},
   333  					0x4: {Timestamp: &timestamppb.Timestamp{Seconds: 4}},
   334  					0x5: {Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   335  					0x6: {Timestamp: &timestamppb.Timestamp{Seconds: 6}},
   336  					0x7: {Timestamp: &timestamppb.Timestamp{Seconds: 7}},
   337  				},
   338  				// next to be written: 0x1 (idx: 1), last written: 0x0 (idx: 0)
   339  				write: 0x1,
   340  			},
   341  			args: args{
   342  				// next to be read: ^uint64(0) (idx: 7), last read: 0xfffffffffffffffe (idx: 6)
   343  				read: ^uint64(0),
   344  			},
   345  			want:    &v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 7}},
   346  			wantErr: nil,
   347  		},
   348  		{
   349  			name: "we overflow write and we are trying to read the 2 previously cycles",
   350  			fields: fields{
   351  				mask:     0x7,
   352  				cycleExp: 0x3, // 7+1=8=2^3
   353  				data: []*v1.Event{
   354  					0x0: {Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   355  					0x1: {Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   356  					0x2: {Timestamp: &timestamppb.Timestamp{Seconds: 2}},
   357  					0x3: {Timestamp: &timestamppb.Timestamp{Seconds: 3}},
   358  					0x4: {Timestamp: &timestamppb.Timestamp{Seconds: 4}},
   359  					0x5: {Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   360  					0x6: {Timestamp: &timestamppb.Timestamp{Seconds: 6}},
   361  					0x7: {Timestamp: &timestamppb.Timestamp{Seconds: 7}},
   362  				},
   363  				// next to be written: 0x8 (idx: 1), last written: 0xffffffffffffffff (idx: 7)
   364  				write: 0x8,
   365  			},
   366  			args: args{
   367  				// next to be read: ^uint64(0)-0x7 (idx: 0), last read: 0xfffffffffffffff7 (idx: 7)
   368  				// read is: ^uint64(0)-0x7 which should represent index 0x0 but
   369  				// with a cycle that was already overwritten
   370  				read: ^uint64(0) - 0x7,
   371  			},
   372  			want: &v1.Event{
   373  				Event: &flowpb.LostEvent{
   374  					Source:        flowpb.LostEventSource_HUBBLE_RING_BUFFER,
   375  					NumEventsLost: 1,
   376  					Cpu:           nil,
   377  				},
   378  			},
   379  			wantErr: nil,
   380  		},
   381  	}
   382  	for _, tt := range tests {
   383  		t.Run(tt.name, func(t *testing.T) {
   384  			r := &Ring{
   385  				mask:      tt.fields.mask,
   386  				data:      tt.fields.data,
   387  				dataLen:   uint64(len(tt.fields.data)),
   388  				cycleExp:  tt.fields.cycleExp,
   389  				cycleMask: ^uint64(0) >> tt.fields.cycleExp,
   390  			}
   391  			r.write.Store(tt.fields.write)
   392  			got, got1 := r.read(tt.args.read)
   393  			if tt.want.GetLostEvent() != nil {
   394  				assert.NotNil(t, got)
   395  				assert.NotNil(t, got.GetLostEvent())
   396  				if diff := cmp.Diff(tt.want.GetLostEvent(), got.GetLostEvent(), cmpopts.IgnoreUnexported(flowpb.LostEvent{})); diff != "" {
   397  					t.Errorf("LostEvent mismatch (-want +got):\n%s", diff)
   398  				}
   399  			} else {
   400  				if !reflect.DeepEqual(got, tt.want) {
   401  					t.Errorf("Ring.read() got = %v, want %v", got, tt.want)
   402  				}
   403  			}
   404  			if !errors.Is(got1, tt.wantErr) {
   405  				t.Errorf("Ring.read() got1 = %v, want %v", got1, tt.wantErr)
   406  			}
   407  		})
   408  	}
   409  }
   410  
   411  func TestRing_Write(t *testing.T) {
   412  	type fields struct {
   413  		len   uint64
   414  		data  []*v1.Event
   415  		write uint64
   416  	}
   417  	type args struct {
   418  		event *v1.Event
   419  	}
   420  	tests := []struct {
   421  		name   string
   422  		fields fields
   423  		want   fields
   424  		args   args
   425  	}{
   426  		{
   427  			name: "normal write",
   428  			args: args{
   429  				event: &v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   430  			},
   431  			fields: fields{
   432  				len:   0x3,
   433  				write: 0,
   434  				data: []*v1.Event{
   435  					0x0: {Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   436  					0x1: {Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   437  					0x2: {Timestamp: &timestamppb.Timestamp{Seconds: 2}},
   438  					0x3: {Timestamp: &timestamppb.Timestamp{Seconds: 3}},
   439  				},
   440  			},
   441  			want: fields{
   442  				len:   0x3,
   443  				write: 1,
   444  				data: []*v1.Event{
   445  					{Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   446  					{Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   447  					{Timestamp: &timestamppb.Timestamp{Seconds: 2}},
   448  					{Timestamp: &timestamppb.Timestamp{Seconds: 3}},
   449  				},
   450  			},
   451  		},
   452  		{
   453  			name: "overflow write",
   454  			args: args{
   455  				event: &v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   456  			},
   457  			fields: fields{
   458  				len:   0x3,
   459  				write: ^uint64(0),
   460  				data: []*v1.Event{
   461  					{Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   462  					{Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   463  					{Timestamp: &timestamppb.Timestamp{Seconds: 2}},
   464  					{Timestamp: &timestamppb.Timestamp{Seconds: 3}},
   465  				},
   466  			},
   467  			want: fields{
   468  				len:   0x3,
   469  				write: 0,
   470  				data: []*v1.Event{
   471  					{Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   472  					{Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   473  					{Timestamp: &timestamppb.Timestamp{Seconds: 2}},
   474  					{Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   475  				},
   476  			},
   477  		},
   478  	}
   479  	for _, tt := range tests {
   480  		t.Run(tt.name, func(t *testing.T) {
   481  			r := &Ring{
   482  				mask: tt.fields.len,
   483  				data: tt.fields.data,
   484  			}
   485  			r.write.Store(tt.fields.write)
   486  			r.Write(tt.args.event)
   487  			want := &Ring{
   488  				mask: tt.want.len,
   489  				data: tt.want.data,
   490  			}
   491  			want.write.Store(tt.want.write)
   492  			reflect.DeepEqual(want, r)
   493  		})
   494  	}
   495  }
   496  
   497  func TestRing_LastWriteParallel(t *testing.T) {
   498  	type fields struct {
   499  		len   uint64
   500  		data  []*v1.Event
   501  		write uint64
   502  	}
   503  	tests := []struct {
   504  		name   string
   505  		fields fields
   506  		want   uint64
   507  	}{
   508  		{
   509  			fields: fields{
   510  				len:   0x3,
   511  				write: 2,
   512  				data:  []*v1.Event{},
   513  			},
   514  			want: 0,
   515  		},
   516  		{
   517  			fields: fields{
   518  				len:   0x3,
   519  				write: 1,
   520  				data:  []*v1.Event{},
   521  			},
   522  			want: ^uint64(0),
   523  		},
   524  	}
   525  	for _, tt := range tests {
   526  		t.Run(tt.name, func(t *testing.T) {
   527  			r := &Ring{
   528  				mask: tt.fields.len,
   529  				data: tt.fields.data,
   530  			}
   531  			r.write.Store(tt.fields.write)
   532  			if got := r.LastWriteParallel(); got != tt.want {
   533  				t.Errorf("Ring.LastWriteParallel() = %v, want %v", got, tt.want)
   534  			}
   535  		})
   536  	}
   537  }
   538  
   539  func TestRing_LastWrite(t *testing.T) {
   540  	type fields struct {
   541  		len   uint64
   542  		data  []*v1.Event
   543  		write uint64
   544  	}
   545  	tests := []struct {
   546  		name   string
   547  		fields fields
   548  		want   uint64
   549  	}{
   550  		{
   551  			fields: fields{
   552  				len:   0x3,
   553  				write: 1,
   554  				data:  []*v1.Event{},
   555  			},
   556  			want: 0,
   557  		},
   558  		{
   559  			fields: fields{
   560  				len:   0x3,
   561  				write: 0,
   562  				data:  []*v1.Event{},
   563  			},
   564  			want: ^uint64(0),
   565  		},
   566  	}
   567  	for _, tt := range tests {
   568  		t.Run(tt.name, func(t *testing.T) {
   569  			r := &Ring{
   570  				mask: tt.fields.len,
   571  				data: tt.fields.data,
   572  			}
   573  			r.write.Store(tt.fields.write)
   574  			if got := r.LastWrite(); got != tt.want {
   575  				t.Errorf("Ring.LastWrite() = %v, want %v", got, tt.want)
   576  			}
   577  		})
   578  	}
   579  }
   580  func TestRingOldestWrite(t *testing.T) {
   581  	r := NewRing(Capacity3)
   582  
   583  	oldestWrite := r.OldestWrite()
   584  	entry, err := r.read(oldestWrite)
   585  	if err != nil {
   586  		t.Errorf("unexpected error: %s", err)
   587  	}
   588  	if entry.GetLostEvent() == nil {
   589  		t.Errorf("able to read oldest entry without prior write: %s", entry)
   590  	}
   591  
   592  	r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 0}})
   593  	r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 1}})
   594  	r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 2}})
   595  	r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 3}})
   596  
   597  	oldestWrite = r.OldestWrite()
   598  	entry, err = r.read(oldestWrite)
   599  	if err != nil {
   600  		t.Errorf("Should be able to read position %x, got %v", oldestWrite, err)
   601  	}
   602  	if entry.Timestamp.Seconds != int64(0) {
   603  		t.Errorf("Read Event should be %+v, got %+v instead (%s)", &timestamppb.Timestamp{Seconds: 0}, entry.Timestamp, entry)
   604  	}
   605  
   606  	// this will trigger a wrap-around and overwrite entry 0
   607  	r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 4}})
   608  
   609  	// the previous oldest entry should now be overwritten
   610  	entry, err = r.read(oldestWrite)
   611  	if err != nil {
   612  		t.Errorf("unexpected error: %s", err)
   613  	}
   614  	if entry.GetLostEvent() == nil {
   615  		t.Errorf("able to read oldest entry even though it has been overwritten: %s", entry)
   616  	}
   617  
   618  	// new oldest entry should have timestamp 1
   619  	oldestWrite = r.OldestWrite()
   620  	entry, err = r.read(oldestWrite)
   621  	if err != nil {
   622  		t.Errorf("Should be able to read position %x, got %v", oldestWrite, err)
   623  	}
   624  	if entry.Timestamp.Seconds != int64(1) {
   625  		t.Errorf("Read Event should be %+v, got %+v instead (%s)", &timestamppb.Timestamp{Seconds: 1}, entry.Timestamp, entry)
   626  	}
   627  }
   628  
   629  func TestRingFunctionalityInParallel(t *testing.T) {
   630  	r := NewRing(Capacity15)
   631  	if len(r.data) != 0x10 {
   632  		t.Errorf("r.data should have a length of 0x10. Got %x", len(r.data))
   633  	}
   634  	if r.mask != 0xf {
   635  		t.Errorf("r.mask should be 0xf. Got %x", r.mask)
   636  	}
   637  	if r.cycleExp != 4 {
   638  		t.Errorf("r.cycleExp should be 4. Got %x", r.cycleExp)
   639  	}
   640  	if r.cycleMask != 0xfffffffffffffff {
   641  		t.Errorf("r.cycleMask should be 0xfffffffffffffff. Got %x", r.cycleMask)
   642  	}
   643  	lastWrite := r.LastWriteParallel()
   644  	if lastWrite != ^uint64(0)-1 {
   645  		t.Errorf("lastWrite should be %x. Got %x", ^uint64(0)-1, lastWrite)
   646  	}
   647  
   648  	r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 0}})
   649  	lastWrite = r.LastWriteParallel()
   650  	if lastWrite != ^uint64(0) {
   651  		t.Errorf("lastWrite should be %x. Got %x", ^uint64(0), lastWrite)
   652  	}
   653  
   654  	r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 1}})
   655  	lastWrite = r.LastWriteParallel()
   656  	if lastWrite != 0x0 {
   657  		t.Errorf("lastWrite should be 0x0. Got %x", lastWrite)
   658  	}
   659  
   660  	entry, err := r.read(lastWrite)
   661  	if err != nil {
   662  		t.Errorf("Should be able to read position %x, got %v", lastWrite, err)
   663  	}
   664  	if entry.Timestamp.Seconds != int64(0) {
   665  		t.Errorf("Read Event should be %+v, got %+v instead", &timestamppb.Timestamp{Seconds: 0}, entry.Timestamp)
   666  	}
   667  	lastWrite--
   668  	lost, _ := r.read(lastWrite)
   669  	if lost.GetLostEvent() == nil {
   670  		t.Errorf("Should not be able to read position %x, got %v", lastWrite, lost)
   671  	}
   672  }
   673  
   674  func TestRingFunctionalitySerialized(t *testing.T) {
   675  	r := NewRing(Capacity15)
   676  	if len(r.data) != 0x10 {
   677  		t.Errorf("r.data should have a length of 0x10. Got %x", len(r.data))
   678  	}
   679  	if r.mask != 0xf {
   680  		t.Errorf("r.mask should be 0xf. Got %x", r.mask)
   681  	}
   682  	lastWrite := r.LastWrite()
   683  	if lastWrite != ^uint64(0) {
   684  		t.Errorf("lastWrite should be %x. Got %x", ^uint64(0)-1, lastWrite)
   685  	}
   686  
   687  	r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 0}})
   688  	lastWrite = r.LastWrite()
   689  	if lastWrite != 0x0 {
   690  		t.Errorf("lastWrite should be %x. Got %x", 0x0, lastWrite)
   691  	}
   692  
   693  	r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: 1}})
   694  	lastWrite = r.LastWrite()
   695  	if lastWrite != 0x1 {
   696  		t.Errorf("lastWrite should be 0x1. Got %x", lastWrite)
   697  	}
   698  
   699  	_, err := r.read(lastWrite)
   700  	if !errors.Is(err, io.EOF) {
   701  		t.Errorf("Should not be able to read position %x, got %v", lastWrite, err)
   702  	}
   703  	lastWrite--
   704  	entry, err := r.read(lastWrite)
   705  	if err != nil {
   706  		t.Errorf("Should be able to read position %x, got %v", lastWrite, err)
   707  	}
   708  	if entry.Timestamp.Seconds != int64(0) {
   709  		t.Errorf("Read Event should be %+v, got %+v instead", &timestamppb.Timestamp{Seconds: 0}, entry.Timestamp)
   710  	}
   711  }
   712  
   713  func TestRing_ReadFrom_Test_1(t *testing.T) {
   714  	defer goleak.VerifyNone(
   715  		t,
   716  		// ignore goroutines started by the redirect we do from klog to logrus
   717  		goleak.IgnoreTopFunction("k8s.io/klog.(*loggingT).flushDaemon"),
   718  		goleak.IgnoreTopFunction("k8s.io/klog/v2.(*loggingT).flushDaemon"),
   719  		goleak.IgnoreTopFunction("io.(*pipe).read"))
   720  	r := NewRing(Capacity15)
   721  	if len(r.data) != 0x10 {
   722  		t.Errorf("r.data should have a length of 0x10. Got %x", len(r.data))
   723  	}
   724  	if r.dataLen != 0x10 {
   725  		t.Errorf("r.dataLen should have a length of 0x10. Got %x", r.dataLen)
   726  	}
   727  	if r.mask != 0xf {
   728  		t.Errorf("r.mask should be 0xf. Got %x", r.mask)
   729  	}
   730  	lastWrite := r.LastWrite()
   731  	if lastWrite != ^uint64(0) {
   732  		t.Errorf("lastWrite should be %x. Got %x", ^uint64(0)-1, lastWrite)
   733  	}
   734  
   735  	// Add 5 events
   736  	for i := uint64(0); i < 5; i++ {
   737  		r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: int64(i)}})
   738  		lastWrite = r.LastWrite()
   739  		if lastWrite != i {
   740  			t.Errorf("lastWrite should be %x. Got %x", i, lastWrite)
   741  		}
   742  	}
   743  
   744  	ctx, cancel := context.WithCancel(context.Background())
   745  	ch := make(chan *v1.Event, 30)
   746  	var wg sync.WaitGroup
   747  	wg.Add(1)
   748  	go func() {
   749  		r.readFrom(ctx, 0, ch)
   750  		wg.Done()
   751  	}()
   752  	i := int64(0)
   753  	for entry := range ch {
   754  		require.NotNil(t, entry)
   755  		want := &timestamppb.Timestamp{Seconds: i}
   756  		if entry.Timestamp.Seconds != want.Seconds {
   757  			t.Errorf("Read Event should be %+v, got %+v instead", want, entry.Timestamp)
   758  		}
   759  		i++
   760  		if i == 4 {
   761  			break
   762  		}
   763  	}
   764  	// next read must be blocked, since ring was not full
   765  	select {
   766  	case entry := <-ch:
   767  		t.Errorf("Read Event %v received when channel should be empty", entry)
   768  	default:
   769  	}
   770  	cancel()
   771  	wg.Wait()
   772  }
   773  
   774  func TestRing_ReadFrom_Test_2(t *testing.T) {
   775  	defer goleak.VerifyNone(
   776  		t,
   777  		// ignore goroutines started by the redirect we do from klog to logrus
   778  		goleak.IgnoreTopFunction("k8s.io/klog.(*loggingT).flushDaemon"),
   779  		goleak.IgnoreTopFunction("k8s.io/klog/v2.(*loggingT).flushDaemon"),
   780  		goleak.IgnoreTopFunction("io.(*pipe).read"))
   781  
   782  	r := NewRing(Capacity15)
   783  	if len(r.data) != 0x10 {
   784  		t.Errorf("r.data should have a length of 0x10. Got %x", len(r.data))
   785  	}
   786  	if r.dataLen != 0x10 {
   787  		t.Errorf("r.dataLen should have a length of 0x10. Got %x", r.dataLen)
   788  	}
   789  	if r.mask != 0xf {
   790  		t.Errorf("r.mask should be 0xf. Got %x", r.mask)
   791  	}
   792  	lastWrite := r.LastWrite()
   793  	if lastWrite != ^uint64(0) {
   794  		t.Errorf("lastWrite should be %x. Got %x", ^uint64(0)-1, lastWrite)
   795  	}
   796  
   797  	// Add 20 events
   798  	for i := uint64(0); i < 20; i++ {
   799  		r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: int64(i)}})
   800  		lastWrite = r.LastWrite()
   801  		if lastWrite != i {
   802  			t.Errorf("lastWrite should be %x. Got %x", i, lastWrite)
   803  		}
   804  	}
   805  
   806  	ctx, cancel := context.WithCancel(context.Background())
   807  	// We should be able to read from a previous 'cycles' and ReadFrom will
   808  	// be able to catch up with the writer.
   809  	ch := make(chan *v1.Event, 30)
   810  	var wg sync.WaitGroup
   811  	wg.Add(1)
   812  	go func() {
   813  		defer wg.Done()
   814  		r.readFrom(ctx, 1, ch)
   815  	}()
   816  	i := int64(1) // ReadFrom
   817  	for event := range ch {
   818  		require.NotNil(t, event)
   819  		// Given the buffer length is 16 and there are no more writes being made,
   820  		// we will receive 15 non-nil events, after that the channel must stall.
   821  		//
   822  		//   ReadFrom +           +----------------valid read------------+  +position possibly being written
   823  		//            |           |                                      |  |  +next position to be written (r.write)
   824  		//            v           V                                      V  V  V
   825  		// write:  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
   826  		// index:  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
   827  		// cycle:  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1
   828  		switch {
   829  		case i < 4:
   830  			want := &flowpb.LostEvent{
   831  				Source:        flowpb.LostEventSource_HUBBLE_RING_BUFFER,
   832  				NumEventsLost: 1,
   833  				Cpu:           nil,
   834  			}
   835  			if diff := cmp.Diff(want, event.GetLostEvent(), cmpopts.IgnoreUnexported(flowpb.LostEvent{})); diff != "" {
   836  				t.Errorf("LostEvent mismatch (-want +got):\n%s", diff)
   837  			}
   838  		default:
   839  			want := &timestamppb.Timestamp{Seconds: i}
   840  			if diff := cmp.Diff(want, event.Timestamp, cmpopts.IgnoreUnexported(timestamppb.Timestamp{})); diff != "" {
   841  				t.Errorf("Event timestamp mismatch (-want +got):\n%s", diff)
   842  			}
   843  		}
   844  		if i == 18 {
   845  			break
   846  		}
   847  		i++
   848  	}
   849  	// next read must be blocked, since reader is waiting for the writer
   850  	select {
   851  	case entry := <-ch:
   852  		t.Errorf("Read Event %v received when channel should be empty", entry)
   853  	default:
   854  	}
   855  
   856  	// Add 20 more events that we read back immediately
   857  	for i := uint64(0); i < 20; i++ {
   858  		r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: int64(20 + i)}})
   859  
   860  		want := &timestamppb.Timestamp{Seconds: int64(20 + (i - 1))}
   861  		entry, ok := <-ch
   862  		if !ok {
   863  			t.Errorf("Channel was have been closed, expected %+v", entry)
   864  		}
   865  		if entry.Timestamp.Seconds != want.Seconds {
   866  			t.Errorf("Read Event should be %+v, got %+v instead", want, entry.Timestamp)
   867  		}
   868  	}
   869  	cancel()
   870  	wg.Wait()
   871  }
   872  
   873  func TestRing_ReadFrom_Test_3(t *testing.T) {
   874  	defer goleak.VerifyNone(
   875  		t,
   876  		// ignore goroutines started by the redirect we do from klog to logrus
   877  		goleak.IgnoreTopFunction("k8s.io/klog.(*loggingT).flushDaemon"),
   878  		goleak.IgnoreTopFunction("k8s.io/klog/v2.(*loggingT).flushDaemon"),
   879  		goleak.IgnoreTopFunction("io.(*pipe).read"))
   880  	r := NewRing(Capacity15)
   881  	if len(r.data) != 0x10 {
   882  		t.Errorf("r.data should have a length of 0x10. Got %x", len(r.data))
   883  	}
   884  	if r.dataLen != 0x10 {
   885  		t.Errorf("r.dataLen should have a length of 0x10. Got %x", r.dataLen)
   886  	}
   887  	if r.mask != 0xf {
   888  		t.Errorf("r.mask should be 0xf. Got %x", r.mask)
   889  	}
   890  	lastWrite := r.LastWrite()
   891  	if lastWrite != ^uint64(0) {
   892  		t.Errorf("lastWrite should be %x. Got %x", ^uint64(0)-1, lastWrite)
   893  	}
   894  
   895  	// Add 20 events
   896  	for i := uint64(0); i < 20; i++ {
   897  		r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: int64(i)}})
   898  		lastWrite = r.LastWrite()
   899  		if lastWrite != i {
   900  			t.Errorf("lastWrite should be %x. Got %x", i, lastWrite)
   901  		}
   902  	}
   903  
   904  	ctx, cancel := context.WithCancel(context.Background())
   905  	// We should be able to read from a previous 'cycles' and ReadFrom will
   906  	// be able to catch up with the writer.
   907  	ch := make(chan *v1.Event, 30)
   908  	var wg sync.WaitGroup
   909  	wg.Add(1)
   910  	go func() {
   911  		r.readFrom(ctx, ^uint64(0)-15, ch)
   912  		wg.Done()
   913  	}()
   914  	i := ^uint64(0) - 15
   915  	for entry := range ch {
   916  		require.NotNil(t, entry)
   917  		// Given the buffer length is 16 and there are no more writes being made,
   918  		// we will receive 15 non-nil events, after that the channel must stall.
   919  		//
   920  		//   ReadFrom +        +-------------------valid read------------+  +position possibly being written
   921  		//            |        |                                         |  |  +next position to be written (r.write)
   922  		//            v        V                                         V  V  V
   923  		// write: f0 f1 //  3  4  5  6  7  8  9  a  b  c  d  e  f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
   924  		// index:  0  1 //  3  4  5  6  7  8  9  a  b  c  d  e  f  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
   925  		// cycle: ff ff //  0  0  0  0  0  0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1
   926  		switch {
   927  		case i < 4, i > 18:
   928  			want := &flowpb.LostEvent{
   929  				Source:        flowpb.LostEventSource_HUBBLE_RING_BUFFER,
   930  				NumEventsLost: 1,
   931  				Cpu:           nil,
   932  			}
   933  			if diff := cmp.Diff(want, entry.GetLostEvent(), cmpopts.IgnoreUnexported(flowpb.LostEvent{})); diff != "" {
   934  				t.Errorf("%d LostEvent mismatch (-want +got):\n%s", i, diff)
   935  			}
   936  		default:
   937  			want := &timestamppb.Timestamp{Seconds: int64(i)}
   938  			if diff := cmp.Diff(want, entry.Timestamp, cmpopts.IgnoreUnexported(timestamppb.Timestamp{})); diff != "" {
   939  				t.Errorf("Event timestamp mismatch (-want +got):\n%s", diff)
   940  			}
   941  		}
   942  		if i == 18 {
   943  			break
   944  		}
   945  		i++
   946  	}
   947  	// next read must be blocked, since reader is waiting for the writer
   948  	select {
   949  	case entry := <-ch:
   950  		t.Errorf("Read Event %v received when channel should be empty", entry)
   951  	default:
   952  	}
   953  
   954  	// Add 20 more events that we read back immediately
   955  	for i := uint64(0); i < 20; i++ {
   956  		r.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: int64(20 + i)}})
   957  
   958  		want := &timestamppb.Timestamp{Seconds: int64(20 + (i - 1))}
   959  		entry, ok := <-ch
   960  		if !ok {
   961  			t.Errorf("Channel was have been closed, expected %+v", entry)
   962  		}
   963  		if entry.Timestamp.Seconds != want.Seconds {
   964  			t.Errorf("Read Event should be %+v, got %+v instead", want, entry.Timestamp)
   965  		}
   966  	}
   967  	cancel()
   968  	wg.Wait()
   969  }