github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/storage/memorystorage/storage_test.go (about)

     1  // Copyright 2022 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package memorystorage
    16  
    17  import (
    18  	"context"
    19  	"encoding"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    24  
    25  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    26  	"github.com/matrixorigin/matrixone/pkg/common/mpool"
    27  	"github.com/matrixorigin/matrixone/pkg/container/batch"
    28  	"github.com/matrixorigin/matrixone/pkg/container/types"
    29  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    30  	"github.com/matrixorigin/matrixone/pkg/pb/timestamp"
    31  	"github.com/matrixorigin/matrixone/pkg/pb/txn"
    32  	"github.com/matrixorigin/matrixone/pkg/testutil"
    33  	"github.com/matrixorigin/matrixone/pkg/vm/engine"
    34  	"github.com/matrixorigin/matrixone/pkg/vm/engine/memoryengine"
    35  	"github.com/stretchr/testify/assert"
    36  )
    37  
    38  func testDatabase(
    39  	t *testing.T,
    40  	newStorage func() (*Storage, error),
    41  ) {
    42  	mp := mpool.MustNewZero()
    43  	ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
    44  	defer cancel()
    45  
    46  	// new
    47  	s, err := newStorage()
    48  	assert.Nil(t, err)
    49  	defer s.Close(ctx)
    50  
    51  	// txn
    52  	txnMeta := txn.TxnMeta{
    53  		ID:     []byte("1"),
    54  		Status: txn.TxnStatus_Active,
    55  		SnapshotTS: timestamp.Timestamp{
    56  			PhysicalTime: 1,
    57  			LogicalTime:  1,
    58  		},
    59  	}
    60  	defer func() {
    61  		_, err := s.Commit(ctx, txnMeta)
    62  		assert.Nil(t, err)
    63  	}()
    64  
    65  	// open database
    66  	{
    67  		resp := &memoryengine.OpenDatabaseResp{}
    68  		err := testRead(
    69  			ctx, t, s, txnMeta,
    70  			memoryengine.OpOpenDatabase,
    71  			&memoryengine.OpenDatabaseReq{
    72  				Name: "foo",
    73  			},
    74  			resp,
    75  		)
    76  		assert.True(t, moerr.IsMoErrCode(err, moerr.ErrNoDB))
    77  	}
    78  
    79  	// create database
    80  	{
    81  		resp := &memoryengine.CreateDatabaseResp{}
    82  		err := testWrite(
    83  			ctx, t, s, txnMeta,
    84  			memoryengine.OpCreateDatabase,
    85  			&memoryengine.CreateDatabaseReq{
    86  				Name: "foo",
    87  			},
    88  			resp,
    89  		)
    90  		assert.Nil(t, err)
    91  		assert.NotEmpty(t, resp.ID)
    92  	}
    93  
    94  	// get databases
    95  	{
    96  		resp := &memoryengine.GetDatabasesResp{}
    97  		err := testRead(
    98  			ctx, t, s, txnMeta,
    99  			memoryengine.OpGetDatabases,
   100  			&memoryengine.GetDatabasesReq{},
   101  			resp,
   102  		)
   103  		assert.Nil(t, err)
   104  		assert.Equal(t, 1, len(resp.Names))
   105  		assert.Equal(t, "foo", resp.Names[0])
   106  	}
   107  
   108  	// open database
   109  	var dbID ID
   110  	{
   111  		resp := &memoryengine.OpenDatabaseResp{}
   112  		err := testRead(
   113  			ctx, t, s, txnMeta,
   114  			memoryengine.OpOpenDatabase,
   115  			&memoryengine.OpenDatabaseReq{
   116  				Name: "foo",
   117  			},
   118  			resp,
   119  		)
   120  		assert.Nil(t, err)
   121  		assert.NotNil(t, resp.ID)
   122  		dbID = resp.ID
   123  
   124  		// delete database
   125  		defer func() {
   126  			{
   127  				resp := &memoryengine.DeleteDatabaseResp{}
   128  				err := testWrite(
   129  					ctx, t, s, txnMeta,
   130  					memoryengine.OpDeleteDatabase,
   131  					&memoryengine.DeleteDatabaseReq{
   132  						Name: "foo",
   133  					},
   134  					resp,
   135  				)
   136  				assert.Nil(t, err)
   137  				assert.NotEmpty(t, resp.ID)
   138  			}
   139  			{
   140  				resp := &memoryengine.GetDatabasesResp{}
   141  				err := testRead(
   142  					ctx, t, s, txnMeta,
   143  					memoryengine.OpGetDatabases,
   144  					&memoryengine.GetDatabasesReq{},
   145  					resp,
   146  				)
   147  				assert.Nil(t, err)
   148  				for _, name := range resp.Names {
   149  					if name == "foo" {
   150  						t.Fatal()
   151  					}
   152  				}
   153  			}
   154  		}()
   155  	}
   156  
   157  	// open relation
   158  	{
   159  		resp := &memoryengine.OpenRelationResp{}
   160  		err := testRead(
   161  			ctx, t, s, txnMeta,
   162  			memoryengine.OpOpenRelation,
   163  			&memoryengine.OpenRelationReq{
   164  				DatabaseID: dbID,
   165  				Name:       "table",
   166  			},
   167  			resp,
   168  		)
   169  		assert.True(t, moerr.IsMoErrCode(err, moerr.ErrNoSuchTable))
   170  	}
   171  
   172  	// create relation
   173  	{
   174  		defA := &engine.AttributeDef{
   175  			Attr: engine.Attribute{
   176  				Name:    "a",
   177  				Type:    types.T_int64.ToType(),
   178  				Primary: true,
   179  			},
   180  		}
   181  		defB := &engine.AttributeDef{
   182  			Attr: engine.Attribute{
   183  				Name:    "b",
   184  				Type:    types.T_int64.ToType(),
   185  				Primary: false,
   186  			},
   187  		}
   188  		defC := &engine.ConstraintDef{
   189  			Cts: []engine.Constraint{
   190  				&engine.PrimaryKeyDef{
   191  					Pkey: &plan.PrimaryKeyDef{
   192  						PkeyColName: "a",
   193  						Names:       []string{"a"},
   194  					},
   195  				},
   196  			},
   197  		}
   198  
   199  		resp := &memoryengine.CreateRelationResp{}
   200  		err := testWrite(
   201  			ctx, t, s, txnMeta,
   202  			memoryengine.OpCreateRelation,
   203  			&memoryengine.CreateRelationReq{
   204  				DatabaseID: dbID,
   205  				Name:       "table",
   206  				Type:       memoryengine.RelationTable,
   207  				Defs: []engine.TableDefPB{
   208  					defA.ToPBVersion(),
   209  					defB.ToPBVersion(),
   210  					defC.ToPBVersion(),
   211  				},
   212  			},
   213  			resp,
   214  		)
   215  		assert.Nil(t, err)
   216  		assert.NotEmpty(t, resp.ID)
   217  	}
   218  
   219  	// get relations
   220  	{
   221  		resp := &memoryengine.GetRelationsResp{}
   222  		err := testRead(
   223  			ctx, t, s, txnMeta,
   224  			memoryengine.OpGetRelations,
   225  			&memoryengine.GetRelationsReq{
   226  				DatabaseID: dbID,
   227  			},
   228  			resp,
   229  		)
   230  		assert.Nil(t, err)
   231  		assert.Equal(t, 1, len(resp.Names))
   232  		assert.Equal(t, "table", resp.Names[0])
   233  	}
   234  
   235  	// open relation
   236  	var relID ID
   237  	{
   238  		resp := &memoryengine.OpenRelationResp{}
   239  		err := testRead(
   240  			ctx, t, s, txnMeta,
   241  			memoryengine.OpOpenRelation,
   242  			&memoryengine.OpenRelationReq{
   243  				DatabaseID: dbID,
   244  				Name:       "table",
   245  			},
   246  			resp,
   247  		)
   248  		assert.Nil(t, err)
   249  		assert.NotNil(t, resp.ID)
   250  		relID = resp.ID
   251  		assert.Equal(t, memoryengine.RelationTable, resp.Type)
   252  	}
   253  	_ = relID
   254  
   255  	// get relation defs
   256  	{
   257  		resp := &memoryengine.GetTableDefsResp{}
   258  		err := testRead(
   259  			ctx, t, s, txnMeta,
   260  			memoryengine.OpGetTableDefs,
   261  			&memoryengine.GetTableDefsReq{
   262  				TableID: relID,
   263  			},
   264  			resp,
   265  		)
   266  		assert.Nil(t, err)
   267  		assert.Equal(t, 3, len(resp.Defs))
   268  	}
   269  
   270  	// write
   271  	{
   272  		colA := testutil.NewVector(
   273  			5,
   274  			types.T_int64.ToType(),
   275  			mp,
   276  			false,
   277  			[]int64{
   278  				1, 2, 3, 4, 5,
   279  			},
   280  		)
   281  		colB := testutil.NewVector(
   282  			5,
   283  			types.T_int64.ToType(),
   284  			mp,
   285  			false,
   286  			[]int64{
   287  				6, 7, 8, 9, 10,
   288  			},
   289  		)
   290  		bat := batch.New(false, []string{"a", "b"})
   291  		bat.Vecs[0] = colA
   292  		bat.Vecs[1] = colB
   293  		bat.SetRowCount(5)
   294  		resp := &memoryengine.WriteResp{}
   295  		err := testWrite(
   296  			ctx, t, s, txnMeta,
   297  			memoryengine.OpWrite,
   298  			&memoryengine.WriteReq{
   299  				TableID: relID,
   300  				Batch:   bat,
   301  			},
   302  			resp,
   303  		)
   304  		assert.Nil(t, err)
   305  	}
   306  
   307  	// read
   308  	var iterID ID
   309  	{
   310  		resp := &memoryengine.NewTableIterResp{}
   311  		err := testRead(
   312  			ctx, t, s, txnMeta,
   313  			memoryengine.OpNewTableIter,
   314  			&memoryengine.NewTableIterReq{
   315  				TableID: relID,
   316  			},
   317  			resp,
   318  		)
   319  		assert.Nil(t, err)
   320  		assert.NotEmpty(t, resp.IterID)
   321  		iterID = resp.IterID
   322  	}
   323  	{
   324  		resp := &memoryengine.ReadResp{}
   325  		err := testRead(
   326  			ctx, t, s, txnMeta,
   327  			memoryengine.OpRead,
   328  			&memoryengine.ReadReq{
   329  				IterID:   iterID,
   330  				ColNames: []string{"a", "b"},
   331  			},
   332  			resp,
   333  		)
   334  		assert.Nil(t, err)
   335  		assert.NotNil(t, resp.Batch)
   336  		assert.Equal(t, 5, resp.Batch.RowCount())
   337  	}
   338  
   339  	// delete by primary key
   340  	{
   341  		colA := testutil.NewVector(
   342  			1,
   343  			types.T_int64.ToType(),
   344  			mp,
   345  			false,
   346  			[]int64{
   347  				1,
   348  			},
   349  		)
   350  		resp := &memoryengine.DeleteResp{}
   351  		err := testWrite(
   352  			ctx, t, s, txnMeta,
   353  			memoryengine.OpDelete,
   354  			&memoryengine.DeleteReq{
   355  				TableID:    relID,
   356  				ColumnName: "a",
   357  				Vector:     colA,
   358  			},
   359  			resp,
   360  		)
   361  		assert.Nil(t, err)
   362  	}
   363  
   364  	// read after delete
   365  	{
   366  		resp := &memoryengine.NewTableIterResp{}
   367  		err := testRead(
   368  			ctx, t, s, txnMeta,
   369  			memoryengine.OpNewTableIter,
   370  			&memoryengine.NewTableIterReq{
   371  				TableID: relID,
   372  			},
   373  			resp,
   374  		)
   375  		assert.Nil(t, err)
   376  		assert.NotEmpty(t, resp.IterID)
   377  		iterID = resp.IterID
   378  	}
   379  	{
   380  		resp := &memoryengine.ReadResp{}
   381  		err := testRead(
   382  			ctx, t, s, txnMeta,
   383  			memoryengine.OpRead,
   384  			&memoryengine.ReadReq{
   385  				IterID:   iterID,
   386  				ColNames: []string{"a", "b"},
   387  			},
   388  			resp,
   389  		)
   390  		assert.Nil(t, err)
   391  		assert.NotNil(t, resp.Batch)
   392  		assert.Equal(t, 4, resp.Batch.RowCount())
   393  	}
   394  
   395  	// delete by non-primary key
   396  	{
   397  		colB := testutil.NewVector(
   398  			1,
   399  			types.T_int64.ToType(),
   400  			mp,
   401  			false,
   402  			[]int64{
   403  				8,
   404  			},
   405  		)
   406  		resp := &memoryengine.DeleteResp{}
   407  		err := testWrite(
   408  			ctx, t, s, txnMeta,
   409  			memoryengine.OpDelete,
   410  			&memoryengine.DeleteReq{
   411  				TableID:    relID,
   412  				ColumnName: "b",
   413  				Vector:     colB,
   414  			},
   415  			resp,
   416  		)
   417  		assert.Nil(t, err)
   418  	}
   419  
   420  	// read after delete
   421  	{
   422  		resp := &memoryengine.NewTableIterResp{}
   423  		err := testRead(
   424  			ctx, t, s, txnMeta,
   425  			memoryengine.OpNewTableIter,
   426  			&memoryengine.NewTableIterReq{
   427  				TableID: relID,
   428  			},
   429  			resp,
   430  		)
   431  		assert.Nil(t, err)
   432  		assert.NotEmpty(t, resp.IterID)
   433  		iterID = resp.IterID
   434  	}
   435  	{
   436  		resp := &memoryengine.ReadResp{}
   437  		err := testRead(
   438  			ctx, t, s, txnMeta,
   439  			memoryengine.OpRead,
   440  			&memoryengine.ReadReq{
   441  				IterID:   iterID,
   442  				ColNames: []string{"a", "b"},
   443  			},
   444  			resp,
   445  		)
   446  		assert.Nil(t, err)
   447  		assert.NotNil(t, resp.Batch)
   448  		assert.Equal(t, 3, resp.Batch.RowCount())
   449  	}
   450  
   451  	// write after delete
   452  	{
   453  		colA := testutil.NewVector(
   454  			1,
   455  			types.T_int64.ToType(),
   456  			mp,
   457  			false,
   458  			[]int64{
   459  				1,
   460  			},
   461  		)
   462  		colB := testutil.NewVector(
   463  			1,
   464  			types.T_int64.ToType(),
   465  			mp,
   466  			false,
   467  			[]int64{
   468  				6,
   469  			},
   470  		)
   471  		bat := batch.New(false, []string{"a", "b"})
   472  		bat.Vecs[0] = colA
   473  		bat.Vecs[1] = colB
   474  		bat.SetRowCount(1)
   475  		resp := &memoryengine.WriteResp{}
   476  		err := testWrite(
   477  			ctx, t, s, txnMeta,
   478  			memoryengine.OpWrite,
   479  			&memoryengine.WriteReq{
   480  				TableID: relID,
   481  				Batch:   bat,
   482  			},
   483  			resp,
   484  		)
   485  		assert.Nil(t, err)
   486  	}
   487  
   488  	// delete relation
   489  	{
   490  		resp := &memoryengine.DeleteRelationResp{}
   491  		err := testWrite(
   492  			ctx, t, s, txnMeta,
   493  			memoryengine.OpDeleteRelation,
   494  			&memoryengine.DeleteRelationReq{
   495  				DatabaseID: dbID,
   496  				Name:       "table",
   497  			},
   498  			resp,
   499  		)
   500  		assert.Nil(t, err)
   501  		assert.NotEmpty(t, resp.ID)
   502  	}
   503  	{
   504  		resp := &memoryengine.GetRelationsResp{}
   505  		err := testRead(
   506  			ctx, t, s, txnMeta,
   507  			memoryengine.OpGetRelations,
   508  			&memoryengine.GetRelationsReq{
   509  				DatabaseID: dbID,
   510  			},
   511  			resp,
   512  		)
   513  		assert.Nil(t, err)
   514  		assert.Equal(t, 0, len(resp.Names))
   515  	}
   516  
   517  	// new relation without primary key
   518  	{
   519  		defA := &engine.AttributeDef{
   520  			Attr: engine.Attribute{
   521  				Name: "a",
   522  				Type: types.T_int64.ToType(),
   523  			},
   524  		}
   525  		defB := &engine.AttributeDef{
   526  			Attr: engine.Attribute{
   527  				Name: "b",
   528  				Type: types.T_int64.ToType(),
   529  			},
   530  		}
   531  
   532  		resp := &memoryengine.CreateRelationResp{}
   533  		err := testWrite(
   534  			ctx, t, s, txnMeta,
   535  			memoryengine.OpCreateRelation,
   536  			&memoryengine.CreateRelationReq{
   537  				DatabaseID: dbID,
   538  				Name:       "table",
   539  				Type:       memoryengine.RelationTable,
   540  				Defs: []engine.TableDefPB{
   541  					defA.ToPBVersion(),
   542  					defB.ToPBVersion(),
   543  				},
   544  			},
   545  			resp,
   546  		)
   547  		assert.Nil(t, err)
   548  		assert.NotEmpty(t, resp.ID)
   549  		relID = resp.ID
   550  	}
   551  
   552  	// write
   553  	{
   554  		colA := testutil.NewVector(
   555  			5,
   556  			types.T_int64.ToType(),
   557  			mp,
   558  			false,
   559  			[]int64{
   560  				1, 2, 3, 4, 5,
   561  			},
   562  		)
   563  		colB := testutil.NewVector(
   564  			5,
   565  			types.T_int64.ToType(),
   566  			mp,
   567  			false,
   568  			[]int64{
   569  				6, 7, 8, 9, 10,
   570  			},
   571  		)
   572  		bat := batch.New(false, []string{"a", "b"})
   573  		bat.Vecs[0] = colA
   574  		bat.Vecs[1] = colB
   575  		bat.SetRowCount(5)
   576  		resp := &memoryengine.WriteResp{}
   577  		err := testWrite(
   578  			ctx, t, s, txnMeta,
   579  			memoryengine.OpWrite,
   580  			&memoryengine.WriteReq{
   581  				TableID: relID,
   582  				Batch:   bat,
   583  			},
   584  			resp,
   585  		)
   586  		assert.Nil(t, err)
   587  	}
   588  
   589  	// delete by primary key
   590  	{
   591  		colA := testutil.NewVector(
   592  			1,
   593  			types.T_int64.ToType(),
   594  			mp,
   595  			false,
   596  			[]int64{
   597  				1,
   598  			},
   599  		)
   600  		resp := &memoryengine.DeleteResp{}
   601  		err := testWrite(
   602  			ctx, t, s, txnMeta,
   603  			memoryengine.OpDelete,
   604  			&memoryengine.DeleteReq{
   605  				TableID:    relID,
   606  				ColumnName: "a",
   607  				Vector:     colA,
   608  			},
   609  			resp,
   610  		)
   611  		assert.Nil(t, err)
   612  	}
   613  
   614  	// read after delete
   615  	{
   616  		resp := &memoryengine.NewTableIterResp{}
   617  		err := testRead(
   618  			ctx, t, s, txnMeta,
   619  			memoryengine.OpNewTableIter,
   620  			&memoryengine.NewTableIterReq{
   621  				TableID: relID,
   622  			},
   623  			resp,
   624  		)
   625  		assert.Nil(t, err)
   626  		assert.NotEmpty(t, resp.IterID)
   627  		iterID = resp.IterID
   628  	}
   629  	{
   630  		resp := &memoryengine.ReadResp{}
   631  		err := testRead(
   632  			ctx, t, s, txnMeta,
   633  			memoryengine.OpRead,
   634  			&memoryengine.ReadReq{
   635  				IterID:   iterID,
   636  				ColNames: []string{"a", "b"},
   637  			},
   638  			resp,
   639  		)
   640  		assert.Nil(t, err)
   641  		assert.NotNil(t, resp.Batch)
   642  		assert.Equal(t, 4, resp.Batch.RowCount())
   643  	}
   644  
   645  	// delete by non-primary key
   646  	{
   647  		colB := testutil.NewVector(
   648  			1,
   649  			types.T_int64.ToType(),
   650  			mp,
   651  			false,
   652  			[]int64{
   653  				8,
   654  			},
   655  		)
   656  		resp := &memoryengine.DeleteResp{}
   657  		err := testWrite(
   658  			ctx, t, s, txnMeta,
   659  			memoryengine.OpDelete,
   660  			&memoryengine.DeleteReq{
   661  				TableID:    relID,
   662  				ColumnName: "b",
   663  				Vector:     colB,
   664  			},
   665  			resp,
   666  		)
   667  		assert.Nil(t, err)
   668  	}
   669  
   670  	// read after delete
   671  	{
   672  		resp := &memoryengine.NewTableIterResp{}
   673  		err := testRead(
   674  			ctx, t, s, txnMeta,
   675  			memoryengine.OpNewTableIter,
   676  			&memoryengine.NewTableIterReq{
   677  				TableID: relID,
   678  			},
   679  			resp,
   680  		)
   681  		assert.Nil(t, err)
   682  		assert.NotEmpty(t, resp.IterID)
   683  		iterID = resp.IterID
   684  	}
   685  	var rowIDs *vector.Vector
   686  	{
   687  		resp := &memoryengine.ReadResp{}
   688  		err := testRead(
   689  			ctx, t, s, txnMeta,
   690  			memoryengine.OpRead,
   691  			&memoryengine.ReadReq{
   692  				IterID:   iterID,
   693  				ColNames: []string{"a", "b", rowIDColumnName},
   694  			},
   695  			resp,
   696  		)
   697  		assert.Nil(t, err)
   698  		assert.NotNil(t, resp.Batch)
   699  		assert.Equal(t, 3, resp.Batch.RowCount())
   700  		rowIDs = resp.Batch.Vecs[2]
   701  	}
   702  
   703  	// delete by row id
   704  	{
   705  		resp := &memoryengine.DeleteResp{}
   706  		err := testWrite(
   707  			ctx, t, s, txnMeta,
   708  			memoryengine.OpDelete,
   709  			&memoryengine.DeleteReq{
   710  				TableID:    relID,
   711  				ColumnName: rowIDColumnName,
   712  				Vector:     rowIDs,
   713  			},
   714  			resp,
   715  		)
   716  		assert.Nil(t, err)
   717  	}
   718  
   719  	// read after delete
   720  	{
   721  		resp := &memoryengine.NewTableIterResp{}
   722  		err := testRead(
   723  			ctx, t, s, txnMeta,
   724  			memoryengine.OpNewTableIter,
   725  			&memoryengine.NewTableIterReq{
   726  				TableID: relID,
   727  			},
   728  			resp,
   729  		)
   730  		assert.Nil(t, err)
   731  		assert.NotEmpty(t, resp.IterID)
   732  		iterID = resp.IterID
   733  	}
   734  	{
   735  		resp := &memoryengine.ReadResp{}
   736  		err := testRead(
   737  			ctx, t, s, txnMeta,
   738  			memoryengine.OpRead,
   739  			&memoryengine.ReadReq{
   740  				IterID:   iterID,
   741  				ColNames: []string{"a", "b", rowIDColumnName},
   742  			},
   743  			resp,
   744  		)
   745  		assert.Nil(t, err)
   746  		assert.Nil(t, resp.Batch)
   747  	}
   748  
   749  	t.Run("duplicated db", func(t *testing.T) {
   750  		tx1 := txn.TxnMeta{
   751  			ID:     []byte("1"),
   752  			Status: txn.TxnStatus_Active,
   753  			SnapshotTS: timestamp.Timestamp{
   754  				PhysicalTime: 1,
   755  				LogicalTime:  1,
   756  			},
   757  		}
   758  		tx2 := txn.TxnMeta{
   759  			ID:     []byte("1"),
   760  			Status: txn.TxnStatus_Active,
   761  			SnapshotTS: timestamp.Timestamp{
   762  				PhysicalTime: 1,
   763  				LogicalTime:  1,
   764  			},
   765  		}
   766  		{
   767  			resp := &memoryengine.CreateDatabaseResp{}
   768  			err := testWrite(
   769  				ctx, t, s, tx1,
   770  				memoryengine.OpCreateDatabase,
   771  				&memoryengine.CreateDatabaseReq{
   772  					Name: "bar",
   773  				},
   774  				resp,
   775  			)
   776  			assert.Nil(t, err)
   777  			assert.NotEmpty(t, resp.ID)
   778  		}
   779  		{
   780  			resp := &memoryengine.CreateDatabaseResp{}
   781  			err := testWrite(
   782  				ctx, t, s, tx2,
   783  				memoryengine.OpCreateDatabase,
   784  				&memoryengine.CreateDatabaseReq{
   785  					Name: "bar",
   786  				},
   787  				resp,
   788  			)
   789  			assert.NotNil(t, err)
   790  		}
   791  	})
   792  
   793  }
   794  
   795  func testRead[
   796  	Resp encoding.BinaryUnmarshaler,
   797  	Req encoding.BinaryMarshaler,
   798  ](
   799  	ctx context.Context,
   800  	t *testing.T,
   801  	s *Storage,
   802  	txnMeta txn.TxnMeta,
   803  	op uint32,
   804  	req Req,
   805  	resp Resp,
   806  ) error {
   807  
   808  	buf, err := req.MarshalBinary()
   809  	if err != nil {
   810  		return err
   811  	}
   812  
   813  	res, err := s.Read(ctx, txnMeta, op, buf)
   814  	if err != nil {
   815  		return err
   816  	}
   817  
   818  	data, err := res.Read()
   819  	if err != nil {
   820  		return err
   821  	}
   822  
   823  	return resp.UnmarshalBinary(data)
   824  }
   825  
   826  func testWrite[
   827  	Resp encoding.BinaryUnmarshaler,
   828  	Req encoding.BinaryMarshaler,
   829  ](
   830  	ctx context.Context,
   831  	t *testing.T,
   832  	s *Storage,
   833  	txnMeta txn.TxnMeta,
   834  	op uint32,
   835  	req Req,
   836  	resp Resp,
   837  ) error {
   838  
   839  	buf, err := req.MarshalBinary()
   840  	if err != nil {
   841  		return err
   842  	}
   843  
   844  	data, err := s.Write(ctx, txnMeta, op, buf)
   845  	if err != nil {
   846  		return err
   847  	}
   848  
   849  	return resp.UnmarshalBinary(data)
   850  }