From 2e664ebafcb2ce50c4c5f2a4b61fd2145ac33411 Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Tue, 5 May 2026 12:16:56 +0300 Subject: [PATCH] api: unexport Stream fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make Stream.Id and Stream.Conn unexported, turning Stream into an opaque handle. This enforces encapsulation and prevents unsafe direct mutation (e.g. stream.Conn = nil). Neither the stream identifier nor the underlying connection is reachable from outside the package — callers should hold their own *Connection reference if they need it. - Rename Stream.Id/Stream.Conn to Stream.id/Stream.conn. - Drop TestStream_IdValues: it only existed to push synthetic stream ids through a Stream; MP_UINT encoding is covered elsewhere. - Document the breaking change in CHANGELOG.md and MIGRATION.md. Closes #471 --- CHANGELOG.md | 3 +++ MIGRATION.md | 16 ++++++++++++++++ connection.go | 4 ++-- stream.go | 8 ++++---- tarantool_test.go | 31 ------------------------------- 5 files changed, 25 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d1dceaca..092c6b70a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,9 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. Use `Opts.Logger *slog.Logger` instead. Pool `Opts.Logger *slog.Logger` replaces direct `log.Printf` calls that were not customizable. By default, logs are discarded (silent). See MIGRATION.md for details. +* `Stream` struct fields `Id` and `Conn` are now unexported, making `Stream` + an opaque handle. Neither the stream identifier nor the underlying + connection is reachable from outside the package (#471). ### Removed diff --git a/MIGRATION.md b/MIGRATION.md index dd33cd4c8..1627b8a79 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -166,6 +166,22 @@ TODO `tarantool.pool` group. When a pool logger is set and a connection does not have its own logger, the pool passes its logger to each connection, which then applies its own `WithGroup("tarantool")`. +* `Stream` struct fields `Id` and `Conn` are unexported, making `Stream` an + opaque handle. The stream identifier and the underlying connection are + internal details; callers should hold their own `*Connection` reference if + they need it. + + Before: + ```go + stream, _ := conn.NewStream() + log.Printf("opened stream %d on %v", stream.Id, stream.Conn) + ``` + + After: + ```go + stream, _ := conn.NewStream() + log.Printf("opened stream on %v", conn) + ``` ## Migration from v1.x.x to v2.x.x diff --git a/connection.go b/connection.go index 328bab4cc..baf3f5cc9 100644 --- a/connection.go +++ b/connection.go @@ -1296,8 +1296,8 @@ func (conn *Connection) NewPrepared(expr string) (*Prepared, error) { func (conn *Connection) NewStream() (*Stream, error) { next := conn.lastStreamId.Add(1) return &Stream{ - Id: next, - Conn: conn, + id: next, + conn: conn, }, nil } diff --git a/stream.go b/stream.go index f57ff212d..1d9436ada 100644 --- a/stream.go +++ b/stream.go @@ -31,8 +31,8 @@ var ( ) type Stream struct { - Id uint64 - Conn *Connection + id uint64 + conn *Connection } // BeginRequest helps you to create a begin request object for execution @@ -241,9 +241,9 @@ func (req *RollbackRequest) Context(ctx context.Context) *RollbackRequest { // create the future. func (s *Stream) Do(req Request) Future { if connectedReq, ok := req.(ConnectedRequest); ok { - if connectedReq.Conn() != s.Conn { + if connectedReq.Conn() != s.conn { return NewFutureWithErr(req, errUnknownStreamRequest) } } - return s.Conn.send(req, s.Id) + return s.conn.send(req, s.id) } diff --git a/tarantool_test.go b/tarantool_test.go index 56dabf6ea..c913f7925 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -7,7 +7,6 @@ import ( "io" "log" "log/slog" - "math" "os" "os/exec" "path/filepath" @@ -2089,36 +2088,6 @@ func TestComplexStructs(t *testing.T) { assert.Equal(t, tuple.Members[1].Name, tuples[0].Members[1].Name) } -func TestStream_IdValues(t *testing.T) { - test_helpers.SkipIfStreamsUnsupported(t) - - conn := test_helpers.ConnectWithValidation(t, dialer, opts) - defer func() { _ = conn.Close() }() - - cases := []uint64{ - 1, - 128, - math.MaxUint8, - math.MaxUint8 + 1, - math.MaxUint16, - math.MaxUint16 + 1, - math.MaxUint32, - math.MaxUint32 + 1, - math.MaxUint64, - } - - stream, _ := conn.NewStream() - req := NewPingRequest() - - for _, id := range cases { - t.Run(fmt.Sprintf("%d", id), func(t *testing.T) { - stream.Id = id - _, err := stream.Do(req).Get() - require.NoError(t, err, "Failed to Ping") - }) - } -} - func TestStream_Commit(t *testing.T) { var req Request var err error