perf(embeds): remove blocking oEmbed calls from request path - #8
Open
Guajir0-code wants to merge 2 commits into
Open
perf(embeds): remove blocking oEmbed calls from request path#8Guajir0-code wants to merge 2 commits into
Guajir0-code wants to merge 2 commits into
Conversation
The app points Eloquent at an existing WordPress database, so no migration creates wp_posts, wp_users, wp_terms and friends. Any test that touched the database therefore could not run, and the single feature test in the repo (GET / asserting 200) failed on a missing database. Add a schema builder for the wp_* tables the app reads, small row builders, and smoke coverage for all seven routes in routes/web.php, including the highlighted-post exclusion, search, and the published/scheduled filtering the global scope is responsible for. withoutVite() keeps the suite independent of `npm run build`. ExampleTest is dropped: RoutesTest covers GET / properly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rendering a post issued up to three synchronous HTTP requests, one per embed provider, each with a 5s timeout, inside the request cycle. They were also effectively uncached on failure: Cache::remember() treats null as a miss, and every one of these helpers returned null when the provider did not answer, so a failing embed was retried on every single request forever. api.instagram.com/oembed in particular has been dead since Meta retired it in 2020, so that path failed 100% of the time. Drop the oEmbed lookups and always emit the markup the provider scripts expect. PostContentText.vue already loads platform.twitter.com/widgets.js, instagram.com/embed.js and embed.reddit.com/widgets.js, which turn that markup into the real embed on the client. This is the same path the old fallback used whenever a request failed, and it is how the YouTube handler has always worked. Net effect: three fewer external dependencies in TTFB, no unbounded retry, and Http, Cache and Log are no longer needed by this service. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Problema
Renderizar um post dispara até três requisições HTTP síncronas para serviços externos, dentro do ciclo da requisição, cada uma com timeout de 5 segundos. E, quando falham, são repetidas em toda requisição, para sempre.
Causa
EmbedProcessorServiceconsultava o oEmbed de Twitter, Instagram e Reddit ao processar o conteúdo:Três problemas se somam.
1. Bloqueia o TTFB
As chamadas acontecem enquanto o Laravel monta a resposta. Um post com os três tipos de embed pode somar 15 segundos ao tempo de resposta se os provedores estiverem lentos — e o visitante espera por isso.
2. O cache negativo não funciona
Cache::remember()tratanullcomo ausência de valor. Como todos esses helpers retornamnullem falha, nada é gravado e a chamada é refeita na requisição seguinte. Não há backoff nem circuit breaker: um provedor fora do ar custa uma tentativa por pageview, indefinidamente.3. O endpoint do Instagram não existe mais
A Meta desativou esse endpoint em 2020; hoje o oEmbed do Instagram exige a Graph API com token. Esse caminho falha 100% das vezes — e, pelo item 2, é retentado 100% das vezes.
Solução
Remover as consultas oEmbed e sempre emitir a marcação que os scripts dos provedores esperam.
Isso não é uma degradação: é o caminho que o código já usava sempre que uma requisição falhava. O fallback existente já produzia exatamente essa marcação, e
PostContentText.vuejá carrega os três scripts que a transformam no embed final:platform.twitter.com/widgets.js,instagram.com/embed.jseembed.reddit.com/widgets.jsrenderizam a partir de<blockquote class="twitter-tweet">,<blockquote class="instagram-media">e<blockquote class="reddit-embed-bq">respectivamente. É o mecanismo oficial de embed dos três.O YouTube sempre funcionou assim — regex direto para
<iframe>, sem nenhuma chamada — e serve de prova de que o padrão se sustenta.Com as chamadas fora,
Http,CacheeLogdeixam de ser necessários neste serviço.Como validar
Cada teste roda com
Http::fake()e umafterEachcomHttp::assertNothingSent().Verificado nos dois sentidos. Contra o código anterior, 5 dos 7 falham por requisições registradas.
Uma nota sobre como esse teste foi escrito
A primeira versão usava
Http::preventStrayRequests()e passava contra o código com o problema. O motivo:preventStrayRequests()lança exceção, e o código antigo envolvia a chamada numtry/catch (\Exception $e)— a exceção era engolida, o fallback entrava, e as asserções sobre a marcação passavam normalmente.Http::fake()grava a requisição em vez de lançar, então a asserção sobrevive aotry/catch. Vale o registro porque o mesmo erro é fácil de repetir em qualquer teste de código defensivo.Impacto
O que se perde
O HTML do oEmbed traz o conteúdo do post já embutido (texto do tweet, contagens), o que dá um render inicial um pouco mais rico quando o serviço responde. Na prática, isso valia para o Twitter e o Reddit, e nunca para o Instagram. Achei uma troca favorável: o ganho aparecia apenas quando o provedor estava rápido, e o custo aparecia sempre.
Se o embed rico do lado do servidor for considerado necessário, o caminho correto é pré-processar fora da requisição — um comando agendado que aquece um cache de HTML já pronto — e não voltar a chamar durante o render.
Fora de escopo