Redis via een VPN voelt traag: minder roundtrips per verzoek
Een VPN maakt van een externe Redis-aanroep geen lokale. Begroot roundtrips per verzoek, hergebruik verbindingen en houd pipelines weg van transacties.
Een pagina die in 200 ms antwoordt op een lokale Redis, doet er vier seconden over zodra de client achter een VPN zit, terwijl de tellers van de server stil blijven staan. Het slow log toont niets boven een milliseconde, de CPU-grafiek is vlak en de instantie bedient andere clients zonder klachten. Wat veranderd is, is de afstand, en wat de afstand vermenigvuldigt, is het aantal keren dat je code stopt en wacht. Loop het pad van het verzoek vanaf de client naar buiten en de oplossing is bijna altijd minder opeenvolgende wachttijden in plaats van een snellere server.
Begin bij de client, niet bij de servermetrieken
Observeer het clientproces tussen het eerste en het laatste commando. Als de trace een antwoord, een pauze en dan het volgende commando laat zien, betaal je per commando een volledige roundtrip en geen enkele servertuning verandert dat. Het slow log is hier het verkeerde instrument, want het registreert uitvoeringstijd en sluit communicatie-I/O bewust uit. Een schoon slow log naast een traag endpoint is de signatuur van een roundtrip-probleem.
Meet het pad zelf voordat je code aanraakt. redis-cli -h redis.example.internal --latency stuurt herhaalde PING's en drukt de gemiddelde verzoek- en antwoordtijd af, dat is je kosten per uitwisseling. Vermenigvuldig dat met het aantal opeenvolgende commando's in één gebruikersactie en je hebt een ondergrens voor het endpoint.
Tel de roundtrips die één gebruikersactie echt nodig heeft
Neem één endpoint en schrijf elke Redis-aanroep op volgorde op, inclusief de aanroepen die verborgen zitten in een ORM, een sessiemiddleware of een feature-flag-lookup. De meeste trage endpoints blijken twintig of dertig aanroepen te doen waar drie genoeg zouden zijn, en de verborgen aanroepen zijn meestal de meerderheid.
Een afhankelijkheidsketen kun je niet samenvouwen: als het tweede commando de sleutel nodig heeft die het eerste teruggeeft, is het wachten echt en zijn de enige opties een ander datamodel of een server-side operatie die de hele stap in één keer uitdrukt. Onafhankelijke leesbewerkingen zijn een ander geval, want niets dwingt je om op het eerste antwoord te wachten voordat je het tweede stuurt. Markeer elke aanroep als afhankelijk of onafhankelijk voordat je iets wijzigt, en de vorm van de oplossing wordt duidelijk.
Het getal dat ertoe doet, is opeenvolgende wachttijden, niet het totale aantal commando's. Dertig commando's in één batch kosten nog steeds één wachttijd.
Batch alleen de commando's die niet van elkaar afhankelijk zijn
Een pipeline zet onafhankelijke commando's in de wachtrij en verzamelt de antwoorden op volgorde, waardoor N wachttijden één worden. De meeste clients bieden het als blok, callback of context manager in plaats van als aparte verbindingsmodus, dus controleer de API die je geïnstalleerde versie levert. De winst schaalt mee met de roundtriptijd, en juist daarom telt het op een VPN en is het op localhost nauwelijks merkbaar.
import redis
r = redis.Redis(host="redis.example.internal", port=6379, socket_timeout=2)
pipe = r.pipeline(transaction=False)
for key in ("user:1001", "user:1002", "user:1003"):
pipe.get(key)
print(pipe.execute())
Geef transaction=False door als je alleen wilt batchen. Standaard wikkelt de client de batch in MULTI en EXEC, wat hem atomair maakt en verandert wat andere clients halverwege kunnen observeren. Atomiciteit is een beslissing over correctheid, geen prestatieknop, en een pipeline is geen transactie alleen omdat beide roundtrips verminderen.
Lees elk antwoord in plaats van alleen het versturen van de batch. Een commando in de wachtrij kan een fout teruggeven terwijl de batch als geheel voltooit, en een client die de antwoordenlijst weggooit, maakt van een mislukte schrijfactie een stille. Vergelijk de lengte van de antwoordenlijst met het aantal commando's dat je in de wachtrij hebt gezet.
Houd de verbinding in leven tussen acties
Elke nieuwe verbinding betaalt voor naamresolutie, een TCP-handshake, een TLS-handshake en een AUTH, en via een VPN is elk daarvan een roundtrip die je niet kunt batchen. Maak de client één keer per proces aan en hergebruik hem, of gebruik de pool die je bibliotheek biedt met een begrensde grootte. Opnieuw verbinden per verzoek is na wachttijden per commando de op een na meest voorkomende oorzaak van dit symptoom.
Houd de private hostnaam in de configuratie in plaats van een letterlijk adres. Certificaatvalidatie en service-identiteit gelden nog steeds op een privaat netwerk, en het vastpinnen van een IP breekt op het moment dat de provider een subnet hernummert. Als resolutie het trage deel is, repareer dan het resolverpad in plaats van de connection string.
Stel een expliciete socket-timeout in zodat een vastgelopen tunnel snel faalt in plaats van een worker te laten hangen. Bevestig daarna dat de pool echt wordt hergebruikt door verbindingsaanmaak in de client te loggen.
Bepaal de batchgrootte voordat het een geheugenprobleem wordt
Redis houdt wachtrij-antwoorden voor de duur van een batch vast, en de client houdt daarna het geparseerde resultaat vast. Een pipeline van tienduizend grote waarden ruilt netwerkwachttijden in voor geheugenpieken en lange pauzes aan beide kanten.
Kies de batchgrootte op basis van antwoordvolume in plaats van commandogetal. Een paar honderd kleine commando's in één batch is meestal comfortabel, honderd payloads van meerdere kilobytes niet. Splits op bytes én op aantal, en houd elke batch binnen het latentiebudget dat het endpoint echt heeft.
Meet end-to-end na de wijziging: redis-cli -h redis.example.internal --latency-history laat zien of het pad zelf stabiel is, en je eigen verzoekstimer laat zien of het endpoint verbeterde. Als het endpoint nog steeds traag is terwijl de latentiehistorie vlak is, is de resterende kostenpost serverwerk of je eigen serialisatie, en de volgende stap is een profiel van de handler in plaats van nog een batchwijziging.