Repository navigation
Await the product counters and cut recently viewed round trips - #968
Merged
Merged
Conversation
added 4 commits
October 21, 2026 16:57
…ry view Updating the row in place overwrote CreatedOnUtc outside the audit clock, could leave the product off the list when a concurrent view trimmed its row, and never shrank a list that was over the limit. The product is inserted before the stale rows are deleted, so it is never missing.
The CustomerId.ProductId index cannot serve the list order, so the customer's list was sorted in memory on every product view.
The counter is statistics only; a failed write is logged instead of failing the page.
The counter is written after the payment is processed and before the order is saved, so a failed write left a paid order unsaved. It is statistics only and is now logged instead.
KrzysztofPajak
marked this pull request as draft
October 21, 2026 15:28
KrzysztofPajak
marked this pull request as ready for review
October 21, 2026 15:28
Comment on lines
+995
to
+998
| catch (Exception exc) | ||
| { | ||
| _logger.LogWarning(exc, "Could not update the sold count of product {ProductId}", product.Id); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A product page view did more work than it needed to:
ProductControllercalled_ = _productService.IncrementProductField(product, x => x.Viewed, 1)inProductDetailsand in the quick-view action. A failed write was an unobserved task exception that nobody saw.PlaceOrderCommandHandlerdid the same with theSoldcounter.AddProductToRecentlyViewedListdeleted the product's row, inserted it again, queried for rows over the limit and then deleted them.CustomerIdand ordered byCreatedOnUtc, but the only compound index isCustomerId, ProductId, CreatedOnUtc.Changes:
Both
Viewedincrements are awaited through aCountProductViewhelper. A failed write is logged as a warning, and the page still renders. It is still one$incper view; the counter is not buffered.The
Soldincrement inPlaceOrderCommandHandleris awaited the same way: a failed write is logged and the order is still saved. The write runs after the payment is processed, so it must not stop the order.AddProductToRecentlyViewedListreads the customer's rows once (Id,ProductId, newest first). It then inserts the viewed product and runs oneDeleteManyAsyncfor:Insert-then-delete means the viewed product is never missing from the list, even when a concurrent view trims its old row.
CreatedOnUtcis always set by the repository's audit clock. The list is trimmed on every view, so a list over the limit (loweredRecentlyViewedProductsNumber, or two concurrent new-product views that deleted the same oldest row) shrinks on the next view.New index
CustomerId.CreatedOnUtc(CustomerIdasc,CreatedOnUtcdesc,_iddesc) onRecentlyViewedProduct. It is created by the installer and by the 2.5 migrationMigrationRecentlyViewedProductCustomerIndex. The existing indexes stay.IRepositoryandIRecentlyViewedProductsServiceare unchanged.ProductControllergets anILogger<ProductController>constructor parameter.Round trips per product view (recently viewed)
Behavior changes
Soldwrite is now logged instead of being lost; order placement is unaffected.Not changed
IncFieldreads, modifies and writes the document, so concurrent views can still lose increments. Awaiting the call does not change that.Testing
RecentlyViewedProductsServiceTests: 10/10. Tests cover:ProductControllerPreviewTests: 7/7. They cover the view being counted once, and the page still rendering when the counter write fails.dotnet test src/Tests/Grand.Business.Checkout.Tests: 292/292. TheSoldguard has no dedicated test, because reachingSaveOrderDetailsneeds a full order setup.Grand.Module.Migrationbuilds.Not done: the races are not covered by unit tests, the migration was not run against a real database, the storefront was not run, and nothing was timed under load.