Add reuse_prototype field to /wiki API endpoint#1082
Conversation
|
|
7c192cd to
148925e
Compare
1e2b090 to
8ac9b3d
Compare
reuse_prototype field to /wiki API endpointreuse_prototype field to /wiki API endpoint
rosalieper
left a comment
There was a problem hiding this comment.
I am please with how this prototype did not turnout as a bigger change
I can't add a "request changes" review as I created the PR. Removing this approval instead. Will create my review comment now.
outdooracorn
left a comment
There was a problem hiding this comment.
From the task's Task Breakdown section:
- We would like to provide an alternative API endpoint so that the Suite team don't rely on an internal API endpoint that we could change at any point. We agreed to use
/reusePrototypewhich points at the samePublicWikiControlleras the internal/wikiendpoint. See routes/api.php#L59.
- If this turns out to be more than copy and editing routes/api.php#L59 - 2hrs of work to write and review, then reconsider
I don't see any changes to routes/api.php or a comment saying that this was attempted but would take too long to implement.
@dati18 you were in the task breakdown meeting where we discussed this. How come it's not included in this PR?
@rosalieper remember, as the reviewer, it's important to read through the task so you know what the PR should contain.
| // Checking relation load state before reading it to avoid N+1 query | ||
| // This relies on the controller to eager load `wikiLatestProfile` relationship | ||
| 'reuse_prototype' => $this->relationLoaded('wikiLatestProfile') |
There was a problem hiding this comment.
This is good to think about to avoid accidentally creating an N+1 query issue in the future. I don't think it requires us to check this every time the toArray() method is called, though. Can we check that PublicWikiController eager loads the relationship in a test instead?
There was a problem hiding this comment.
We might want to create a PublicWikiControllerTest unit test for this check. The current PublicWikiTest looks more like an e2e test which is not where I would expect this type of check to go. My hunch is that it will be easier to test in a unit test as well.
There was a problem hiding this comment.
I somehow stashed the change and didn't notice. I also made a change to show() to make sure wikiLatestProfile was eager-loaded...
This is a stupid fault. I apologize. :(
There was a problem hiding this comment.
I added a unit test for the relation check
There was a problem hiding this comment.
based on your comment, I did the following:
- added the endpoint
/reusePrototypeand also made sureshow()eager-loads'wikiLatestProfile' - added PublicWikiControllerTest unit test
- modified the
reusePrototypeTestby changing the endpoint toreusePrototypeinstead of genericthis->route, also extended it to also callGET /reusePrototype/id
There was a problem hiding this comment.
If we can create a successful test to check for eager loading, then we can remove this check here.
0d903dc to
976f2d9
Compare
@outdooracorn Thank you for your comment, I read through the task description twice actually because it has a long description with updates and followup comment. Thanks for catching this. It left my mind at some point during the review but I will remember this incident. :) |
| */ | ||
| public function show($id) { | ||
| return new PublicWikiResource(Wiki::findOrFail($id)); | ||
| return new PublicWikiResource(Wiki::query()->with('wikiLatestProfile')->findOrFail($id)); |
There was a problem hiding this comment.
Same as $query in index(), I think we can benefit from eager loading here
There was a problem hiding this comment.
What makes you think that? Did you count the number of DB queries like I did in https://phabricator.wikimedia.org/T421877#11810168?
I'm wondering that as we are only getting a single Wiki, rather than a list of Wikis, if the N+1 issue won't appear.
There was a problem hiding this comment.
I do get this error when I checkout the PR, composer install, rebuild the container and then visit
Can you pasted the command? I just run composer install and didn't see similar error. I tore down everything and rebuilt/reinstalled also...
There was a problem hiding this comment.
I'm wondering that as we are only getting a single Wiki, rather than a list of Wikis, if the N+1 issue won't appear.
Ah you're right, it got me right there... since show() returns a single wiki, worst-case scenario is wikiLatestProfile was lazy-loaded and we do 1 extra query.
So I have 2 options:
- Keep it for the sake of explicitness/consistency and avoid hidden lazy loading.
- Reverse it back because its impact is very much minimal
What do you think?
There was a problem hiding this comment.
@rosalieper your opinion is also valuable as well :)
There was a problem hiding this comment.
My vote is to remove it from this PR as:
- it is not required
- it is not related to the task (T421877)
It's good to keep PRs small and to the point where possible.
outdooracorn
left a comment
There was a problem hiding this comment.
Sorry, I didn't have enough time to provide a full review 🥲
I do get this error when I checkout the PR, composer install, rebuild the container and then visit http://localhost:8082/wiki?sort=pages&direction=desc&page=1&per_page=10:
public/index.php:55
The stream or file "/var/www/html/storage/logs/laravel-2026-05-29.log" could not be opened in append mode: Failed to open stream: Permission denied The exception occurred while attempting to log: The stream or file "/var/www/html/storage/logs/laravel-2026-05-29.log" could not be opened in append mode: Failed to open stream: Permission denied The exception occurred while attempting to log: The stream or file "/var/www/html/storage/logs/laravel-2026-05-29.log" could not be opened in append mode: Failed to open stream: Permission denied The exception occurred while attempting to log: The stream or file "/var/www/html/storage/logs/laravel-2026-05-29.log" could not be opened in append mode: Failed to open stream: Permission denied The exception occurred while attempting to log: The stream or file "/var/www/html/storage/logs/laravel-2026-05-29.log" could not be opened in append mode: Failed to open stream: Permission denied The exception occurred while attempting to log: The stream or file "/var/www/html/storage/logs/laravel-2026-05-29.log" could not be opened in append mode: Failed to open stream: Permission denied The exception occurred while attempting to log: Query Executed: Context: {"sql":"select count(*) as aggregate from `wikis` where `wikis`.`deleted_at` is null","bindings":[],"connection":"mysql"} Context: {"exception":{}} Context: {"exception":{}} Context: {"exception":{}} Context: {"exception":{}} Context: {"exception":{}}
This doesn't happen when I checkout origin/main, composer install, rebuild the container, and then visit http://localhost:8082/wiki?sort=pages&direction=desc&page=1&per_page=10. So there is likely something for you to look into here. Hopefully I haven't done anything silly locally! 🙈
| */ | ||
| public function show($id) { | ||
| return new PublicWikiResource(Wiki::findOrFail($id)); | ||
| return new PublicWikiResource(Wiki::query()->with('wikiLatestProfile')->findOrFail($id)); |
There was a problem hiding this comment.
What makes you think that? Did you count the number of DB queries like I did in https://phabricator.wikimedia.org/T421877#11810168?
I'm wondering that as we are only getting a single Wiki, rather than a list of Wikis, if the N+1 issue won't appear.
outdooracorn
left a comment
There was a problem hiding this comment.
I tested again locally and couldn't reproduce my previous error, so not sure what the issue was there. Sorry for the noise.
| class PublicWikiControllerTest extends TestCase { | ||
| use DatabaseTransactions; | ||
|
|
||
| public function testShowEagerLoadsWikiLatestProfileForResource(): void { |
There was a problem hiding this comment.
This test passes even if wikiLatestProfile isn't eager loaded.
The PublicWikiResource will lazy load the model if it hasn't already been loaded so $this->assertTrue($resource->resource->relationLoaded('wikiLatestProfile')); will always be true.
Looking at PublicWikiController a bit closer, it might be harder than I initially thought to test this.
EDIT: the above is incorrect.
You mentioned in another comment about creating a test queries counter and ensure 'wiki_profiles' is hit only once. Is this something that Laravel provides helpers for? This might be useful if not too difficult to implement.
There was a problem hiding this comment.
Are you sure?
If I change show() to lazy load the test fails... :(
public function show($id) {
return new PublicWikiResource(Wiki::query()->findOrFail($id));
}public function show($id) {
return new PublicWikiResource(Wiki::findOrFail($id));
}There was a problem hiding this comment.
Oh! I see what happened here, I removed the eager loading from the index() method as that is the method that requires eager loading to avoid N+1 issues. I hadn't noticed you wrote this test only for the show() method. Why aren't testing the index() method?
There was a problem hiding this comment.
Ah yes... index() returns a collection of wikis so it's worth testing than show()
I just added a test for index() by by generating a random n number of wikis (from 3 to 100) and checking for the number of queries (it should be 1) and making sure wikiLatestProfile is eager-loaded.
There was a problem hiding this comment.
fun read about relation loaded and stuff
There was a problem hiding this comment.
Cool that you have figured out how to count the SQL queries executed.
However, there are some things I don't like about this new test:
- Tests should be deterministic - given the same code and environment, they should behave the same way every time they run. Creating a random number of Wikis is not deterministic. I would create 3 - 5 wikis for this test.
- Tests should not
echoor otherwise print output. Instead, they should communicate results through assertions, exceptions, and PHPUnit's output mechanisms
| // Checking relation load state before reading it to avoid N+1 query | ||
| // This relies on the controller to eager load `wikiLatestProfile` relationship | ||
| 'reuse_prototype' => $this->relationLoaded('wikiLatestProfile') |
There was a problem hiding this comment.
If we can create a successful test to check for eager loading, then we can remove this check here.
a714fa9 to
e9c7a31
Compare
e9c7a31 to
bdd52e8
Compare
| if ($this->app->environment('local')) { | ||
| \Event::listen(QueryExecuted::class, function (QueryExecuted $query) { | ||
| \Log::debug('Query Executed: ', [ | ||
| 'sql' => $query->sql, | ||
| 'bindings' => $query->bindings, | ||
| 'connection' => $query->connectionName, | ||
| ]); | ||
| }); | ||
| } |
There was a problem hiding this comment.
The stream or file "/var/www/html/storage/logs/laravel-2026-05-29.log" could not be opened in append mode: Failed to open stream: Permission denied
I got the above error again and tracked it down to:
- some of the logs in
/var/www/html/storage/logs/were owned byroot:rootrather thanwww-data:www-data - this event listening doing a log
I don't think it's an issue in staging/production as I don't think logging to a file is enabled.
There was a problem hiding this comment.
ah I just checked and had the same issue. But you've already specified "local"-only so I don't think it's going to be a problem on staging/prod
6d415c7 to
0657731
Compare
outdooracorn
left a comment
There was a problem hiding this comment.
As requested by @dati18, not a full review. Mostly comments around the PublicWikiControllerTest.
| }); | ||
|
|
||
| $controller = new PublicWikiController; | ||
| $resourceCollection = $controller->index(request()); |
There was a problem hiding this comment.
The request() method this for getting "an instance of the current request or an input item from the request" which does feel quite right to use here. I'd prefer to create an empty Request instance instead.
| $resourceCollection = $controller->index(request()); | |
| $resourceCollection = $controller->index(new Request()); |
| $resourceCollection = $controller->index(request()); | ||
|
|
||
| $this->assertSame(1, $profileQueryCount); | ||
| $this->assertTrue($resourceCollection->collection[0]->resource->relationLoaded('wikiLatestProfile')); |
There was a problem hiding this comment.
Optional nitpick: if you want, you can use first() instead of collection[0] and ->resource isn't required.
| $this->assertTrue($resourceCollection->collection[0]->resource->relationLoaded('wikiLatestProfile')); | |
| $this->assertTrue($resourceCollection->first()->relationLoaded('wikiLatestProfile')); |
| class PublicWikiControllerTest extends TestCase { | ||
| use DatabaseTransactions; | ||
|
|
||
| public function testShowEagerLoadsWikiLatestProfileForResource(): void { |
There was a problem hiding this comment.
Cool that you have figured out how to count the SQL queries executed.
However, there are some things I don't like about this new test:
- Tests should be deterministic - given the same code and environment, they should behave the same way every time they run. Creating a random number of Wikis is not deterministic. I would create 3 - 5 wikis for this test.
- Tests should not
echoor otherwise print output. Instead, they should communicate results through assertions, exceptions, and PHPUnit's output mechanisms
| 'domain' => 'index-eager-load-test-' . $i . '.wikibase.cloud', | ||
| 'sitename' => 'Index Eager Load Test Site ' . $i, |
There was a problem hiding this comment.
Nitpick: We are using a PHP version that supports string interpolation so lets use it as it reduces the line length. I also find it much easier to read.
| 'domain' => 'index-eager-load-test-' . $i . '.wikibase.cloud', | |
| 'sitename' => 'Index Eager Load Test Site ' . $i, | |
| 'domain' => "index-eager-load-test-{$i}.wikibase.cloud", | |
| 'sitename' => "Index Eager Load Test Site {$i}", |
| */ | ||
| public function show($id) { | ||
| return new PublicWikiResource(Wiki::findOrFail($id)); | ||
| return new PublicWikiResource(Wiki::query()->with('wikiLatestProfile')->findOrFail($id)); |
There was a problem hiding this comment.
My vote is to remove it from this PR as:
- it is not required
- it is not related to the task (T421877)
It's good to keep PRs small and to the point where possible.
When
->with('wikiLatestProfile')is used in thePublicWikiControllerthis change only results in 1 extra database query per API request.Bug: T421877