-
Notifications
You must be signed in to change notification settings - Fork 730
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
[POC/RFC] Extract Connection logic out of the Client #1070
Conversation
This also somehow overlaps with #944 In the end Elastica itself should not have to handle the complexity of connectors. I'm not even sure if it will need a connector interface, all it needs to do is the forward configuration options. This PR seems to be from my point of view almost too radical as it seems to break even the simplest usage of Elastica\Client as far as I understand. I would actually hope that the implementation of #944 makes it much more obvious how we could proceed here and remove all the connection dependencies. |
Yeah, I agree this is radical. I'd expect to back it off and make it simpler to actually use as a next step, it should be possible to achieve without breaking BC for normal users. Mostly just some tinkering I did while sorting out the other PRs. |
Cool, I really like to discuss new ideas based on PR's as it often says more then describing it. |
a63ac88
to
8720d86
Compare
I've improved a bit more on this proposal: First Commit (Interfaces) Second Commit (Deprecations) Third Commit (Extracting ConnectionPool creation) Fourth Commit (Tests) |
@ruflin Did you disable travis as well? |
aea63ce
to
fc6e0c5
Compare
@merk At least not intentionally :-( I only made changes on codecov.io so it should not have any affects :-( |
A few pushes here didnt seem to trigger travis builds :\ |
Yeah, not sure why :-( Just checked the configs on travis and all looks good. |
After running the docker tests, there dont appear to be any failures (after some slight adjustments of the test cases). I've pushed a new commit that fixes test cases and documents the BC break (and removes the offending test). This is the test on master: https://github.com/ruflin/Elastica/blob/master/test/lib/Elastica/Test/ClientTest.php#L1176-1192 |
7174f66
to
0404b4d
Compare
I think we should proceed with these changes. I had a look again and I think it is partially going to overlap with #944 but at the same time it could actually simplify #944 . Not sure yet and also not sure when I get the time to do #944 so we should not be blocked by it. Is the CHANGELOG still up-to-date? Can you rebase on master? This should hopefully also solve the travis build issue and make starting ES more stable. |
This does simplify moving to php-elasticsearch because ultimately you'd just need to wrap the php-es Client with a wrapper that implements I believe this is still up to date, I'll take a look at master when I get home tonight and do a rebase. Only thing to note here is I think this PR should cause a minor bump, not a patch bump. |
To note, this reverses #1077 |
0404b4d
to
40287f3
Compare
Rebased. But something to consider before merging this is that we've removed the $config array methods from the Client which is a hard BC break. These methods don't have an analog anymore since the $config array is immediately consumed in the creation of the ConnectionPool and cannot be changed after initialisation. We can either implement these methods as a noop with a deprecation/php notice, by throwing an exception with more information, or by leaving them removed. |
40287f3
to
be03d99
Compare
@merk Two notes:
|
@merk It seems one of the connection tests is broken. |
Regarding #1077, no - the concept of changing the runtime configuration of the Elastica Client is no longer present at all and the mechanism that connect() was added for is no longer supported as per the documented BC break. Theoretically, if someone wanted to emulate the behaviour of changing connection details after a Client is created, they would need to implement their own ConnectionPoolInterface which can do whatever the implementor wants, as long as getConnection() returns a ConnectionInterface. I might be able to do something to keep a bit more BC in terms of the getConfigValue/setConfigValue methods, but it will increase the complexity of the ConnectionPool quite a bit. Give me a few days to think about it. |
@aguvillalba Have a look at the comments above, would be good to have you in this conversation. @merk An alternative idea here is that instead of having something like connect, is that it is possible to assign a new ConnectionPool to the client. So if someone wants to add / remove connections at a later stage, he has to exchange the complete Connection Pool. |
If you know ahead of time that you need custom connection pool functionality, it isnt very complicated to pass a ConnectionPoolInterface implementation that is going to handle changing connection details - it isnt really necessary functionality to supply in the default implementation. We could have a second ConnectionPoolInterface that handles this separately - but my thinking right now to try to avoid BC breaks might revolve around providing a more advanced ConnectionPool that we might need to ship anyway. |
Another thing to consider is if the Interfaces that have been added are optimal: if we create these interfaces they become codified for use by others. The whole split of Connection/Transport becomes something that needs to be supported. It might be worth considering a simplified approach? |
As you know, I'm less hesitant to break BC then you are if it is the right way to go in the long run. So the question I ask myself, what is going to happen to Connection Pool and Transport when we get elasticsearch-php in? TBH I haven't thought this part through yet :-( |
Hi @ruflin and @merk! I understand and completely agree with the idea of taking out from the Client all the logic to connect to the server.
We should do:
And after this, we could use the $client as usually, is it right? Or what would be the client's API to create an "empty" client and inject the connection settings later? Thank you both for these improvements! |
Right now, this PR would require either setting the config array as previously supported in the Client Constructor: $client = new Client(['servers' => /*...*/]); Or setting a ConnectionPool: $connections = []; // An array of ConnectionInterface objects
$pool = new ConnectionPool($connections)
$client = new Client($pool); There is no method for creating an empty Client. It is possible to achieve what you're asking for by creating a custom ConnectionPool which can have its connections modified later, but the Client requires a reference to that pool on creation. |
But this would make the Client less flexible, wouldn't it? You are forcing the developer to know all the connection details (either the array or the pool) before creating the client instead of making them optional and set the connection details later. There are cases where the developer needs to set the connection details after creating the client. |
In order to not make my app dependent on Elastica, I created an ElasticaClientAdapter which implements a SearchClient interface. So my app actually uses a SearchClient (today is Elastica, tomorrow could be some other and I do not have to change my app code, only the adapter).
As I said before, making the library (exactly the Client) more flexible does not imply less maintainable code. |
Your dependency injection container is the correct place (which will already know everything required) to manage the creation and configuration of the Client and its Connections. Alternatively, as I've stated multiple times, a custom ConnectionPool can be used. It is no more onerous as your SearchClient. And even then, your SearchClient could manage the lifecycle here itself and create the Elastica client when you have the information required. And third, as this PR demonstrates - following the SRP and reducing the edge cases that the Client is required to follow results in a reduction of code complexity both in lines and mental model required to maintain the code. |
8a1f8fd
to
53551ce
Compare
Ok, I do not agree at all with:
Flexibility does not imply complex code, as I said, it depends on how you design it.
Based on that, should I also put my connection parameters in the dependency injection container? Not in the app config? Anyway, @ruflin, it's ok, it's your library and you decide its route map. I think I will stick to the version 3.2 which is the one that works fine for me and maybe it's a good chance to start my own package from now on. Thank you very much for your effort! |
@ruflin rebased, Callback test fixed |
I've added a new commit with a LegacyConnectionPool that should theoretically support the getConfig_/setConfig_ methods on the Client in a BC way, but because there is no functional or unit test coverage for the expected behaviours it might be subtly different. If this is actually something to be merged, test coverage has to be modified and the LegacyConnectionPool will need a full suite of tests depending on if it is a temporary stopgap until a major bump or if it is going to stay around forever. I think the only thing missing is working through how a new Client should be 'officially' created and tweaking the Client constructor or Builder class to provide a factory if it suits. |
bc7d30b
to
cb1e2ca
Compare
@aguvillalba Thanks a lot for your inputs. I think the client is currently under flux to make it more reusable for others to extend. To go the right path, it is important to gather together the different facts and opinions. I believe only in combining them we will find the best solution. Sometimes if we break stuff, we also have to go backwards and figure out new ways. Please keep pushing for your ideas. :-) |
@merk I really like the addition of the LegacyConnectionPool. I enables us to do the restructuring withouth breaking BC and it shows and example how the ConnectionPool interface can be used. +1 on moving forward here. |
So I've spent some time thinking about official 'how to start a Client' documentation, a few options: We leave the $config array the default recommended way of configuring the Client and rely on the LegacyConnectionPool. The deprecated methods on the Client get removed in 4.0 but they can still be accessed on the pool itself. We have a ClientFactory that returns a configured Client, which could look something like: class ClientFactory {
public function create(array $connections = [], $strategy, $logger) {
// handle creation of a normal pool and default to a
// localhost connection if no $connections
} Introduce developers to the actual way that Elastica works internally (my preferred option) $connections = [];
$pool = new ConnectionPool($connections);
$client = new Client($pool, $logger); |
And looking at the Client constructor I want to deprecate the $callback option as well, since if someone wants to have a callback run on failure they can wrap or implement their own ConnectionPool instead. We could even leave the callback in the LegacyPool |
@merk Not so sure about the settings removal. I see it reasonable to remove most of the options and move it to the connection pool. But for example passing an array of hosts would be really nice. The reason is mainly simplicity as it feels for me quite intuitive that you tell a client just to connect to a list of hosts and the rest happens magically. An alternative could be to do some magic in the contstructor dependent on which types are passed, means allowing both. |
@merk I think it is now also time to push this one forward again. How should we proceed? Perhaps it still makes sense to do a quick video call to discuss it all in person? |
I liked the idea @ruflin , such a shame the PR got lost from 2016! |
As part of the SOLID principles is the Single Responsibility principle which I try to follow in designing services. It is my believe the Client shouldnt know anything about the internal workings of the ConnectionPool.
In this PR, I've removed quite a bit of "magic" and made creation of Connection objects and the ConnectionPool explicit - but this is about starting a discussion (along with #1069) about direction. I would expect to add helpers to simplify the default setup of these classes for 90% of the use cases encountered.
Is this something I should explore further and get to a working state where tests pass?