Issue 1: Why...so...slow?
We can easily see that our homepage is running slow. It takes a long time to load, and clicking on the “Browse Featured Products” takes an awkward amount of time. Let’s open the Frontend Insights page in Sentry to see if we can find out what’s going on.
Investigating the issue
Let’s click into it, go to the “Sampled Events” tab, and select an event from the list.
We can see that the FCP and LCP web vitals are high, and a bunch of resource.link
spans that load images, but we can’t see what’s going on in the backend before rendering the page.
To connect the backend and frontend with tracing, we need to add the tracing header to the app.blade.php
view in the head
tag:
{!! \Sentry\Laravel\Integration::sentryMeta() !!}
This will add three headers to the page which will let the frontend SDK continue the trace the the backend started. Once we refresh and go back to Sentry, we’ll see that the browser.request
span has children spans now:
The Laravel SDK has automatically instrumented a big part of the backend, so that’s why we can see all these spans. The db.sql.query
spans are what we’re interested in, but since Sentry doesn’t capture the actual data returned from the database, we’re going to have to add a custom span.
The http.route
span tells us that the request is being handled by the index
method of the ProductController
, so let’s get in there and wrap the database queries with a custom span that adds some extra debugging information:
public function index(){ // Check if we're in a trace $parent = \Sentry\SentrySDK::getCurrentHub()->getSpan(); $span = null;
if ($parent !== null) { // Start a new span context and add the span operation and description $context = \Sentry\Tracing\SpanContext::make() ->setOp('function') ->setDescription('Fetch products');
// Start a child span with the context we just created $span = $parent->startChild($context);
\Sentry\SentrySDK::getCurrentHub()->setSpan($span); }
// Fetch the products (unchanged) $featuredProducts = Product::select()->where('featured', 1)->get(); $allProducts = Product::all();
// Add some data to the span, like the number of all products and featured products we fetched from the database $span?->setData(['numFeaturedProducts' => count($featuredProducts), 'numAllProducts' => count($allProducts)]); // Make sure to finish the span, otherwise it won't get reported $span?->finish();
return Inertia::render('index', ['featuredProducts' => $featuredProducts, 'allProducts' => $allProducts]);}
If we refresh the page and go back to Sentry, we’ll see the new Fetch products
span along with the data we added to it:
We’re apparently sending 3000 products to the frontend, which is way too much to be displayed in one page. Aside from the title, category, and price, each product also loads its own image. The DOM hates us right now.
Fixing the issue
To fix this, we’ll either need to implement pagination, either with pages or with a “load more” button. That’s a bit too much for this workshop, so let’s just grab the first 30 products instead of all of them:
$allProducts = Product::all()->take(30);
If we refresh the page, we’ll notice that the page loads much faster, the “Browse Featured Products” button doesn’t lag, and the number of products in the Fetch products
span is now much lower:
Great! Let’s move on to the next issue!