github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/tests/integration/topic_transactions_test.go (about)

     1  //go:build integration
     2  // +build integration
     3  
     4  package integration
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"io"
    10  	"os"
    11  	"strconv"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/version"
    19  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
    20  	"github.com/ydb-platform/ydb-go-sdk/v3/query"
    21  	"github.com/ydb-platform/ydb-go-sdk/v3/topic/topicwriter"
    22  )
    23  
    24  func TestTopicReadInTransaction(t *testing.T) {
    25  	if os.Getenv("YDB_VERSION") != "nightly" && version.Lt(os.Getenv("YDB_VERSION"), "25.0") {
    26  		t.Skip("require enables transactions for topics")
    27  	}
    28  	scope := newScope(t)
    29  	ctx := scope.Ctx
    30  	require.NoError(t, scope.TopicWriter().Write(ctx, topicwriter.Message{Data: strings.NewReader("asd")}))
    31  	scope.Logf("topic message written")
    32  
    33  	require.NoError(t, scope.Driver().Query().DoTx(ctx, func(ctx context.Context, tx query.TxActor) error {
    34  		reader := scope.TopicReaderNamed("first")
    35  		scope.Logf("trying to pop a batch")
    36  		batch, err := reader.PopMessagesBatchTx(ctx, tx)
    37  		scope.Logf("pop a batch result: %v", err)
    38  		if err != nil {
    39  			return err
    40  		}
    41  		content := string(xtest.Must(io.ReadAll(batch.Messages[0])))
    42  		require.Equal(t, "asd", content)
    43  		_ = reader.Close(ctx)
    44  		return nil
    45  	}))
    46  
    47  	scope.Logf("first pop messages done")
    48  
    49  	scope.Logf("writting second message")
    50  	require.NoError(t, scope.TopicWriter().Write(ctx, topicwriter.Message{Data: strings.NewReader("bbb")}))
    51  
    52  	require.NoError(t, scope.Driver().Query().DoTx(ctx, func(ctx context.Context, tx query.TxActor) error {
    53  		reader := scope.TopicReaderNamed("second")
    54  		// err := tx.Exec(ctx, "SELECT 1", query.WithCommit())
    55  		err := tx.Exec(ctx, "SELECT 1")
    56  		if err != nil {
    57  			return err
    58  		}
    59  
    60  		scope.Logf("trying second pop batch")
    61  		batch, err := reader.PopMessagesBatchTx(ctx, tx)
    62  		scope.Logf("second pop batch result: %v", err)
    63  		if err != nil {
    64  			return err
    65  		}
    66  		content := string(xtest.Must(io.ReadAll(batch.Messages[0])))
    67  		require.Equal(t, "bbb", content)
    68  		return nil
    69  	}))
    70  }
    71  
    72  func TestWriteInTransaction(t *testing.T) {
    73  	if os.Getenv("YDB_VERSION") != "nightly" && version.Lt(os.Getenv("YDB_VERSION"), "25.0") {
    74  		t.Skip("require enables transactions for topics")
    75  	}
    76  
    77  	t.Run("OK", func(t *testing.T) {
    78  		scope := newScope(t)
    79  		reader := scope.TopicReader()
    80  
    81  		driver := scope.DriverWithGRPCLogging()
    82  
    83  		const writeTime = time.Second
    84  		protectCtx, cancel := context.WithTimeout(scope.Ctx, writeTime*10)
    85  		defer cancel()
    86  
    87  		deadline := time.Now().Add(writeTime)
    88  		transactionsCount := 0
    89  		for {
    90  			err := driver.Query().DoTx(scope.Ctx, func(ctx context.Context, tx query.TxActor) error {
    91  				writer, err := driver.Topic().StartTransactionalWriter(tx, scope.TopicPath())
    92  				if err != nil {
    93  					return err
    94  				}
    95  
    96  				return writer.Write(ctx, topicwriter.Message{Data: strings.NewReader(strconv.Itoa(transactionsCount))})
    97  			})
    98  			require.NoError(t, err)
    99  			transactionsCount++
   100  			if time.Now().After(deadline) {
   101  				break
   102  			}
   103  		}
   104  
   105  		for i := 0; i < transactionsCount; i++ {
   106  			mess, err := reader.ReadMessage(protectCtx)
   107  			require.NoError(t, err)
   108  
   109  			contentBytes, _ := io.ReadAll(mess)
   110  			content := string(contentBytes)
   111  			require.Equal(t, strconv.Itoa(i), content)
   112  		}
   113  		t.Logf("transactions count: %v", transactionsCount)
   114  	})
   115  
   116  	t.Run("Rollback", func(t *testing.T) {
   117  		scope := newScope(t)
   118  		reader := scope.TopicReader()
   119  
   120  		driver := scope.Driver()
   121  
   122  		const writeTime = time.Second
   123  		protectCtx, cancel := context.WithTimeout(scope.Ctx, writeTime*10)
   124  		defer cancel()
   125  
   126  		deadline := time.Now().Add(writeTime)
   127  		transactionsCount := 0
   128  		testErr := errors.New("test")
   129  		for {
   130  			err := driver.Query().DoTx(scope.Ctx, func(ctx context.Context, tx query.TxActor) error {
   131  				writer, err := driver.Topic().StartTransactionalWriter(tx, scope.TopicPath())
   132  				if err != nil {
   133  					return err
   134  				}
   135  
   136  				require.NoError(t, writer.Write(ctx, topicwriter.Message{Data: strings.NewReader(strconv.Itoa(transactionsCount))}))
   137  				return testErr
   138  			})
   139  			require.ErrorIs(t, err, testErr)
   140  			transactionsCount++
   141  			if time.Now().After(deadline) {
   142  				break
   143  			}
   144  		}
   145  
   146  		protectCtx, cancel = context.WithTimeout(scope.Ctx, time.Millisecond*10)
   147  		defer cancel()
   148  
   149  		mess, err := reader.ReadMessage(protectCtx)
   150  		require.ErrorIs(t, err, context.DeadlineExceeded)
   151  		_ = mess
   152  		t.Logf("transactions count: %v", transactionsCount)
   153  	})
   154  }