-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature/box: first box and box.info implementation #414
base: master
Are you sure you want to change the base?
Conversation
c643841
to
5d9ad18
Compare
implemented the box interface for tarantool with a small number of fields, which in the future can be supplemented
5d9ad18
to
e6327cd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with this approach is that it cannot be extended. As example:
- How to use a context with the method? How to cancel the request?
- How to make an async request?
It's better to implement a custom request type, as it is already done in the crud
and the arrow
subpackages. It is possible to implement a custom response type for a request too:
Line 577 in 8cf8673
exResp, ok := resp.(*tarantool.ExecuteResponse) |
box/info.go
Outdated
// Cluster information, including cluster UUID. | ||
Cluster ClusterInfo `msgpack:"cluster"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the Tarantool 3 the field has name replicaset
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there should be a custom decoder for this type or extend the type to avoid problems, this implementation is rather minimal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, that is, I propose in this case, for example, to remove this field altogether, leaving a minimal non-conflicting implementation
I absolutely agree about the context and even think that it should be mandatory when calling any method, but I see the asynchronous implementation of using box as redundant, the asynchronous implementation may be cheaper than a goroutine, but still I mean the complexity of using asynchronous calls to the future |
Maybe the user will use it this way, and maybe he will want it differently. And considering that we are developing a public library, I am not so sure about the answer. In any case, I would prefer to do it a way that is already implemented in the |
yes, I understand, but the ease of use of the box is lost in this case, I understood the fixes and am implementing them. I just wanted to note that, just like an active user of the library, it would be easier for me to add a context to the Info() signature, if necessary, launch a goroutine |
I agree that it is easier at the moment, but not in the long term. As example, think about adding context here to all these methods with backward compatibility. Lines 20 to 128 in 8cf8673
API with requests vs API with ready to call methods a much easier to maintain and to extend over time. |
@oleg-jukovec i added request and response types with tarantool.Request face, so sugar logic just use them and wrap to more easy logical struct |
@DerekBum , please also look at pr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the patch!
I agree with the idea, let's just resolve the comments.
// Box defines an interface for interacting with a Tarantool instance. | ||
// It includes the Info method, which retrieves instance information. | ||
type Box interface { | ||
Info() (Info, error) // Retrieves detailed information about the Tarantool instance. | ||
} | ||
|
||
// box is a concrete implementation of the Box interface. | ||
// It holds a connection to the Tarantool instance via the Doer interface. | ||
type box struct { | ||
conn tarantool.Doer // Connection interface for interacting with Tarantool. | ||
} | ||
|
||
// By returns a new instance of the box structure, which implements the Box interface. | ||
func By(conn tarantool.Doer) Box { | ||
return &box{ | ||
conn: conn, // Assigns the provided Tarantool connection. | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to work here without an interface with just a concrete implementation.
// Box defines an interface for interacting with a Tarantool instance. | |
// It includes the Info method, which retrieves instance information. | |
type Box interface { | |
Info() (Info, error) // Retrieves detailed information about the Tarantool instance. | |
} | |
// box is a concrete implementation of the Box interface. | |
// It holds a connection to the Tarantool instance via the Doer interface. | |
type box struct { | |
conn tarantool.Doer // Connection interface for interacting with Tarantool. | |
} | |
// By returns a new instance of the box structure, which implements the Box interface. | |
func By(conn tarantool.Doer) Box { | |
return &box{ | |
conn: conn, // Assigns the provided Tarantool connection. | |
} | |
} | |
// Box is a helper that wraps for box.* requests. | |
// It holds a connection to the Tarantool instance via the Doer interface. | |
type Box struct { | |
conn tarantool.Doer // Connection interface for interacting with Tarantool. | |
} | |
// By returns a new instance of the box structure, which implements the Box interface. | |
func New(conn tarantool.Doer) Box { | |
return &Box{ | |
conn: conn, // Assigns the provided Tarantool connection. | |
} | |
} |
|
||
func Example() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, add a separate example for the Box.Info() too.
// Info retrieves the current information of the Tarantool instance. | ||
// It calls the "box.info" function and parses the result into the Info structure. | ||
func (b *box) Info() (Info, error) { | ||
var infoResp InfoResponse | ||
|
||
// Call "box.info" to get instance information from Tarantool. | ||
fut := b.conn.Do(NewInfoRequest()) | ||
|
||
// Parse the result into the Info structure. | ||
err := fut.GetTyped(&infoResp) | ||
if err != nil { | ||
return Info{}, err | ||
} | ||
|
||
// Return the parsed info and any potential error. | ||
return infoResp.Info, err | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, move it to box.go
. The request/response is ok here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> request.go
// check all fields run correctly | ||
_, err = uuid.Parse(info.UUID) | ||
require.NoErrorf(t, err, "validate instance uuid is valid") | ||
|
||
require.NotEmpty(t, info.Version) | ||
// check that pid parsed correctly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// check all fields run correctly | |
_, err = uuid.Parse(info.UUID) | |
require.NoErrorf(t, err, "validate instance uuid is valid") | |
require.NotEmpty(t, info.Version) | |
// check that pid parsed correctly | |
// Check all fields run correctly. | |
_, err = uuid.Parse(info.UUID) | |
require.NoErrorf(t, err, "validate instance uuid is valid") | |
require.NotEmpty(t, info.Version) | |
// Check that pid parsed correctly. |
// ClusterInfo represents information about the cluster. | ||
// It contains the unique identifier (UUID) of the cluster. | ||
type ClusterInfo struct { | ||
UUID string `msgpack:"uuid"` | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it used anywhere?
// The node ID (nullable). | ||
ID *int `msgpack:"id"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm not mistaken replicaset id starts from 1, so 0 is a valid-nil value. If I'm wrong, there's no need to fix it.
// The node ID (nullable). | |
ID *int `msgpack:"id"` | |
// The node ID (nullable). | |
ID int `msgpack:"id"` |
LSN uint64 `msgpack:"lsn"` | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With a custom decoder you could be able to decode replicaset.uuid and cluster.uuid at a same field. But I don't mind if it isn't implemented here.
if err != nil { | ||
return err | ||
} | ||
|
||
if arrayLen != 1 { | ||
return fmt.Errorf("protocol violation; expected 1 array entry, got %d", arrayLen) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could add tests for this cases.
// Type returns IPROTO type for request. | ||
func (req baseRequest) Type() iproto.Type { | ||
return req.impl.Type() | ||
} | ||
|
||
// Ctx returns a context of request. | ||
func (req baseRequest) Ctx() context.Context { | ||
return req.impl.Ctx() | ||
} | ||
|
||
// Async returns request expects a response. | ||
func (req baseRequest) Async() bool { | ||
return req.impl.Async() | ||
} | ||
|
||
// Response creates a response for the baseRequest. | ||
func (req baseRequest) Response(header tarantool.Header, | ||
body io.Reader) (tarantool.Response, error) { | ||
return req.impl.Response(header, body) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And a test for that too.
I implemented the box interface for tarantool with a small number of fields, which in the future can be supplemented
I didn't forget about (remove if it is not applicable):
Related issues: #410