forked from fl00r/go-tarantool-1.6
    
        
        - 
                Notifications
    You must be signed in to change notification settings 
- Fork 60
Open
Description
Overview
We are considering a significant design change for the upcoming v3 release of the Tarantool Go client: replacing the current concrete Future struct with an interface. This change aims to improve testability, enable better abstraction, and support more flexible usage patterns—while carefully preserving performance.
This issue is a great opportunity for interns to understand Go interface design, API evolution, and trade-offs between abstraction and performance.
Current State
Right now, tarantool.Future is defined as a concrete struct with internal fields and public methods:
type Future struct {
    // unexported fields
}It exposes the following public methods:
// NewFuture creates a new empty Future for a given Request.
func NewFuture(req Request) *Future
// WaitChan returns a channel that closes when the response arrives or an error occurs.
func (fut *Future) WaitChan() <-chan struct{}
// GetTyped waits for the Future and decodes the response directly into 'result' using msgpack.
// This avoids intermediate []interface{} allocation and is faster than Get().
func (fut *Future) GetTyped(result interface{}) error
// Get waits for the Future and returns data as []interface{} + error.
// Slower than GetTyped due to generic decoding.
func (fut *Future) Get() ([]interface{}, error)
// GetResponse returns the raw Response and error.
func (fut *Future) GetResponse() (Response, error)
// These methods are used internally by the client to fulfill the Future:
func (fut *Future) SetError(err error)
func (fut *Future) SetResponse(header Header, body io.Reader) errorProblem Statement
- Hard to mock in tests: Because Futureis a concrete type with internal state and private fields, it’s difficult to create lightweight mocks or stubs for unit testing code that depends on it.
- Leaky abstraction: Public setters like SetErrorandSetResponseexpose internal mechanics that users shouldn’t need to interact with—these are really meant for internal use by the connection logic.
- Tight coupling: Code that consumes a *Futureis tightly coupled to this specific implementation, limiting flexibility (e.g., you can’t easily substitute a fake or instrumented future).
Proposed Change (for v3)
Refactor Future into an interface that captures only the observable behavior users need:
type Future interface {
	WaitChan() <-chan struct{}
	Get() ([]interface{}, error)
	GetTyped(result interface{}) error
	GetResponse() (Response, error)
}- Remove SetErrorandSetResponsefrom the public API (they become internal implementation details).
- Keep the existing struct (possibly renamed to futureordefaultFuture) as the default implementation of this interface.
- Update NewFuture(or introduce a new factory) to return the interface:func NewFuture(req Request) Future 
Benefits
- Testability: Users (and our own tests) can easily create mock Futureimplementations that return predefined responses or simulate errors—without spinning up a real connection or dealing with channels/mutexes.
- Cleaner API: Hides internal mutation methods (Set*) that were never meant for public use.
- Extensibility: Enables alternative Futureimplementations (e.g., for observability, caching, or async backends) without breaking user code.
Concerns & Considerations
- Performance: Adding an interface indirection might introduce a tiny overhead. However:
- Modern Go compilers optimize interface calls well.
- The cost is likely negligible compared to network I/O and msgpack decoding.
- We should benchmark before/after (interns: this is a great task!).
 
- Naming: Should the interface be called Future, and the concrete typefuture(unexported)?
- Go way: It may be agains go way "call by interfaces, return structs".
Tasks
-  Draft the new Futureinterface.
- Refactor the existing struct into an unexported implementation.
- Update all internal usage to work with the interface.
-  Write unit tests that mock the Futureinterface.
-  Benchmark Get(),GetTyped(), andWaitChan()before/after to verify no significant regression (pprof, go test -cpuprofile, -memprofile, go benches).
- Update documentation and examples.
Resources
Metadata
Metadata
Assignees
Labels
No labels