VictoriaMetrics/lib/persistentqueue/fastqueue_test.go
Nikolay 090cb2c9de
app/vmagent: allow to disabled on-disk persistence (#5088)
* app/vmagent: allow to disabled on-disk queue
Previously, it wasn't possible to build data processing pipeline with a
chain of vmagents. In case when remoteWrite for the last vmagent in the
chain wasn't accessible, it persisted data only when it has enough disk
capacity. If disk queue is full, it started to silently drop ingested
metrics.

New flags allows to disable on-disk persistent and immediatly return an
error if remoteWrite is not accessible anymore. It blocks any writes and
notify client, that data ingestion isn't possible.

Main use case for this feature - use external queue such as kafka for
data persistence.
https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2110

* adds test, updates readme

* apply review suggestions

* update docs for vmagent

* makes linter happy

---------

Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
2023-11-24 13:42:11 +01:00

370 lines
8.7 KiB
Go

package persistentqueue
import (
"fmt"
"sync"
"testing"
"time"
)
func TestFastQueueOpenClose(_ *testing.T) {
path := "fast-queue-open-close"
mustDeleteDir(path)
for i := 0; i < 10; i++ {
fq := MustOpenFastQueue(path, "foobar", 100, 0, true)
fq.MustClose()
}
mustDeleteDir(path)
}
func TestFastQueueWriteReadInmemory(t *testing.T) {
path := "fast-queue-write-read-inmemory"
mustDeleteDir(path)
capacity := 100
fq := MustOpenFastQueue(path, "foobar", capacity, 0, true)
if n := fq.GetInmemoryQueueLen(); n != 0 {
t.Fatalf("unexpected non-zero inmemory queue size: %d", n)
}
var blocks []string
for i := 0; i < capacity; i++ {
block := fmt.Sprintf("block %d", i)
if !fq.WriteBlock([]byte(block)) {
t.Fatalf("unexpected false for WriteBlock")
}
blocks = append(blocks, block)
}
if n := fq.GetInmemoryQueueLen(); n != capacity {
t.Fatalf("unexpected size of inmemory queue; got %d; want %d", n, capacity)
}
for _, block := range blocks {
buf, ok := fq.MustReadBlock(nil)
if !ok {
t.Fatalf("unexpected ok=false")
}
if string(buf) != block {
t.Fatalf("unexpected block read; got %q; want %q", buf, block)
}
}
fq.MustClose()
mustDeleteDir(path)
}
func TestFastQueueWriteReadMixed(t *testing.T) {
path := "fast-queue-write-read-mixed"
mustDeleteDir(path)
capacity := 100
fq := MustOpenFastQueue(path, "foobar", capacity, 0, false)
if n := fq.GetPendingBytes(); n != 0 {
t.Fatalf("the number of pending bytes must be 0; got %d", n)
}
var blocks []string
for i := 0; i < 2*capacity; i++ {
block := fmt.Sprintf("block %d", i)
if !fq.WriteBlock([]byte(block)) {
t.Fatalf("not expected WriteBlock fail")
}
blocks = append(blocks, block)
}
if n := fq.GetPendingBytes(); n == 0 {
t.Fatalf("the number of pending bytes must be greater than 0")
}
for _, block := range blocks {
buf, ok := fq.MustReadBlock(nil)
if !ok {
t.Fatalf("unexpected ok=false")
}
if string(buf) != block {
t.Fatalf("unexpected block read; got %q; want %q", buf, block)
}
}
if n := fq.GetPendingBytes(); n != 0 {
t.Fatalf("the number of pending bytes must be 0; got %d", n)
}
fq.MustClose()
mustDeleteDir(path)
}
func TestFastQueueWriteReadWithCloses(t *testing.T) {
path := "fast-queue-write-read-with-closes"
mustDeleteDir(path)
capacity := 100
fq := MustOpenFastQueue(path, "foobar", capacity, 0, false)
if n := fq.GetPendingBytes(); n != 0 {
t.Fatalf("the number of pending bytes must be 0; got %d", n)
}
var blocks []string
for i := 0; i < 2*capacity; i++ {
block := fmt.Sprintf("block %d", i)
if !fq.WriteBlock([]byte(block)) {
t.Fatalf("unexpected false for WriteBlock")
}
blocks = append(blocks, block)
fq.MustClose()
fq = MustOpenFastQueue(path, "foobar", capacity, 0, false)
}
if n := fq.GetPendingBytes(); n == 0 {
t.Fatalf("the number of pending bytes must be greater than 0")
}
for _, block := range blocks {
buf, ok := fq.MustReadBlock(nil)
if !ok {
t.Fatalf("unexpected ok=false")
}
if string(buf) != block {
t.Fatalf("unexpected block read; got %q; want %q", buf, block)
}
fq.MustClose()
fq = MustOpenFastQueue(path, "foobar", capacity, 0, false)
}
if n := fq.GetPendingBytes(); n != 0 {
t.Fatalf("the number of pending bytes must be 0; got %d", n)
}
fq.MustClose()
mustDeleteDir(path)
}
func TestFastQueueReadUnblockByClose(t *testing.T) {
path := "fast-queue-read-unblock-by-close"
mustDeleteDir(path)
fq := MustOpenFastQueue(path, "foorbar", 123, 0, false)
resultCh := make(chan error)
go func() {
data, ok := fq.MustReadBlock(nil)
if ok {
resultCh <- fmt.Errorf("unexpected ok=true")
return
}
if len(data) != 0 {
resultCh <- fmt.Errorf("unexpected non-empty data=%q", data)
return
}
resultCh <- nil
}()
fq.MustClose()
select {
case err := <-resultCh:
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
case <-time.After(time.Second):
t.Fatalf("timeout")
}
mustDeleteDir(path)
}
func TestFastQueueReadUnblockByWrite(t *testing.T) {
path := "fast-queue-read-unblock-by-write"
mustDeleteDir(path)
fq := MustOpenFastQueue(path, "foobar", 13, 0, false)
block := "foodsafdsaf sdf"
resultCh := make(chan error)
go func() {
data, ok := fq.MustReadBlock(nil)
if !ok {
resultCh <- fmt.Errorf("unexpected ok=false")
return
}
if string(data) != block {
resultCh <- fmt.Errorf("unexpected block read; got %q; want %q", data, block)
return
}
resultCh <- nil
}()
if !fq.WriteBlock([]byte(block)) {
t.Fatalf("unexpected false for WriteBlock")
}
select {
case err := <-resultCh:
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
case <-time.After(time.Second):
t.Fatalf("timeout")
}
fq.MustClose()
mustDeleteDir(path)
}
func TestFastQueueReadWriteConcurrent(t *testing.T) {
path := "fast-queue-read-write-concurrent"
mustDeleteDir(path)
fq := MustOpenFastQueue(path, "foobar", 5, 0, false)
var blocks []string
blocksMap := make(map[string]bool)
var blocksMapLock sync.Mutex
for i := 0; i < 1000; i++ {
block := fmt.Sprintf("block %d", i)
blocks = append(blocks, block)
blocksMap[block] = true
}
// Start readers
var readersWG sync.WaitGroup
for i := 0; i < 10; i++ {
readersWG.Add(1)
go func() {
defer readersWG.Done()
for {
data, ok := fq.MustReadBlock(nil)
if !ok {
return
}
blocksMapLock.Lock()
if !blocksMap[string(data)] {
panic(fmt.Errorf("unexpected data read from the queue: %q", data))
}
delete(blocksMap, string(data))
blocksMapLock.Unlock()
}
}()
}
// Start writers
blocksCh := make(chan string)
var writersWG sync.WaitGroup
for i := 0; i < 10; i++ {
writersWG.Add(1)
go func() {
defer writersWG.Done()
for block := range blocksCh {
if !fq.WriteBlock([]byte(block)) {
t.Errorf("unexpected false for WriteBlock")
return
}
}
}()
}
// feed writers
for _, block := range blocks {
blocksCh <- block
}
close(blocksCh)
// Wait for writers to finish
writersWG.Wait()
// wait for a while, so readers could catch up
time.Sleep(100 * time.Millisecond)
// Close fq
fq.MustClose()
// Wait for readers to finish
readersWG.Wait()
// Collect the remaining data
fq = MustOpenFastQueue(path, "foobar", 5, 0, false)
resultCh := make(chan error)
go func() {
for len(blocksMap) > 0 {
data, ok := fq.MustReadBlock(nil)
if !ok {
resultCh <- fmt.Errorf("unexpected ok=false")
return
}
if !blocksMap[string(data)] {
resultCh <- fmt.Errorf("unexpected data read from fq: %q", data)
return
}
delete(blocksMap, string(data))
}
resultCh <- nil
}()
select {
case err := <-resultCh:
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
case <-time.After(time.Second * 5):
t.Fatalf("timeout")
}
fq.MustClose()
mustDeleteDir(path)
}
func TestFastQueueWriteReadWithDisabledPQ(t *testing.T) {
path := "fast-queue-write-read-inmemory-disabled-pq"
mustDeleteDir(path)
capacity := 20
fq := MustOpenFastQueue(path, "foobar", capacity, 0, true)
if n := fq.GetInmemoryQueueLen(); n != 0 {
t.Fatalf("unexpected non-zero inmemory queue size: %d", n)
}
var blocks []string
for i := 0; i < capacity; i++ {
block := fmt.Sprintf("block %d", i)
if !fq.WriteBlock([]byte(block)) {
t.Fatalf("unexpected false for WriteBlock")
}
blocks = append(blocks, block)
}
if fq.WriteBlock([]byte("error-block")) {
t.Fatalf("expect false due to full queue")
}
fq.MustClose()
fq = MustOpenFastQueue(path, "foobar", capacity, 0, true)
for _, block := range blocks {
buf, ok := fq.MustReadBlock(nil)
if !ok {
t.Fatalf("unexpected ok=false")
}
if string(buf) != block {
t.Fatalf("unexpected block read; got %q; want %q", buf, block)
}
}
fq.MustClose()
mustDeleteDir(path)
}
func TestFastQueueWriteReadWithIgnoreDisabledPQ(t *testing.T) {
path := "fast-queue-write-read-inmemory-disabled-pq-force-write"
mustDeleteDir(path)
capacity := 20
fq := MustOpenFastQueue(path, "foobar", capacity, 0, true)
if n := fq.GetInmemoryQueueLen(); n != 0 {
t.Fatalf("unexpected non-zero inmemory queue size: %d", n)
}
var blocks []string
for i := 0; i < capacity; i++ {
block := fmt.Sprintf("block %d", i)
if !fq.WriteBlock([]byte(block)) {
t.Fatalf("unexpected false for WriteBlock")
}
blocks = append(blocks, block)
}
if fq.WriteBlock([]byte("error-block")) {
t.Fatalf("expect false due to full queue")
}
for i := 0; i < capacity; i++ {
block := fmt.Sprintf("block %d-%d", i, i)
fq.MustWriteBlockIgnoreDisabledPQ([]byte(block))
blocks = append(blocks, block)
}
fq.MustClose()
fq = MustOpenFastQueue(path, "foobar", capacity, 0, true)
for _, block := range blocks {
buf, ok := fq.MustReadBlock(nil)
if !ok {
t.Fatalf("unexpected ok=false")
}
if string(buf) != block {
t.Fatalf("unexpected block read; got %q; want %q", buf, block)
}
}
fq.MustClose()
mustDeleteDir(path)
}