ZaStoGram/TMessagesProj/jni/tgnet/BuffersStorage.cpp
loop-uh ebb82aef03 Ходить к веб-релеям по IPv6, когда он доступен
Резолвер запрашивал только записи A, поэтому список IPv6 всегда оставался
пустым и клиент не мог воспользоваться шестым протоколом даже там, где он
есть. Между тем у релеев DC1 и DC5 IPv4-адрес единственный и в сетях
пользователей не отвечает вовсе: в логе 22 таймаута подряд на стадии
установки TCP к kws1-1 и kws5-1, ни одного успешного подключения. Эмодзи и
реакции лежат как раз на этих датацентрах, отсюда жалоба «фото грузятся, а
эмодзи нет».

По IPv6 те же релеи отвечают без потерь, и адресов там сразу два против
одного у IPv4 — то есть протокол даёт и обход блокировки, и запасной адрес.
Теперь AAAA запрашивается отдельным запросом (только при наличии у
устройства собственного глобального IPv6, иначе выдавать такой адрес
значило бы заменить рабочий путь мёртвым), IPv6 выбирается первым, а
кандидаты перебираются по кругу: повторное разрешение имени означает, что
предыдущий адрес не сработал, и следующая попытка обязана прийтись на
другого кандидата.

Маршрут WSS при доступном IPv6 строится по имени релея, а не по зашитому
IPv4-адресу, иначе резолвер вообще не вызывается.

Заодно свёрнут спам аллокаций буферов: он давал больше тысячи строк на лог
и вытеснял полезные записи.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 15:31:09 +03:00

174 lines
6.2 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* This is the source code of tgnet library v. 1.1
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2015-2018.
*/
#include "BuffersStorage.h"
#include "FileLog.h"
#include "NativeByteBuffer.h"
#include "ConnectionsManager.h"
#include <time.h>
namespace {
const int64_t BUFFER_POOL_PRESSURE_LOG_INTERVAL_MS = 5000;
const int64_t BUFFER_POOL_SUMMARY_INTERVAL_MS = 60 * 1000;
uint32_t bufferPoolMaxCountForCapacity(uint32_t capacity) {
if (capacity == 8 || capacity == 128 || capacity == 1024 + 200) {
return 80;
}
return 10;
}
int64_t bufferPoolMonotonicMillis() {
struct timespec timeSpec;
clock_gettime(CLOCK_MONOTONIC, &timeSpec);
return (int64_t) timeSpec.tv_sec * 1000 + timeSpec.tv_nsec / 1000000;
}
}
BuffersStorage &BuffersStorage::getInstance() {
static BuffersStorage instance(true);
return instance;
}
BuffersStorage::BuffersStorage(bool threadSafe) {
isThreadSafe = threadSafe;
if (isThreadSafe) {
pthread_mutex_init(&mutex, NULL);
}
for (uint32_t a = 0; a < 4; a++) {
freeBuffers8.push_back(new NativeByteBuffer((uint32_t) 8));
}
for (uint32_t a = 0; a < 5; a++) {
freeBuffers128.push_back(new NativeByteBuffer((uint32_t) 128));
}
}
NativeByteBuffer *BuffersStorage::getFreeBuffer(uint32_t size) {
uint32_t byteCount = 0;
std::vector<NativeByteBuffer *> *arrayToGetFrom = nullptr;
NativeByteBuffer *buffer = nullptr;
if (size <= 8) {
arrayToGetFrom = &freeBuffers8;
byteCount = 8;
} else if (size <= 128) {
arrayToGetFrom = &freeBuffers128;
byteCount = 128;
} else if (size <= 1024 + 200) {
arrayToGetFrom = &freeBuffers1024;
byteCount = 1024 + 200;
} else if (size <= 4096 + 200) {
arrayToGetFrom = &freeBuffers4096;
byteCount = 4096 + 200;
} else if (size <= 16384 + 200) {
arrayToGetFrom = &freeBuffers16384;
byteCount = 16384 + 200;
} else if (size <= 40000) {
arrayToGetFrom = &freeBuffers32768;
byteCount = 40000;
} else if (size <= 160000) {
arrayToGetFrom = &freeBuffersBig;
byteCount = 160000;
} else {
buffer = new NativeByteBuffer(size);
}
if (arrayToGetFrom != nullptr) {
if (isThreadSafe) {
pthread_mutex_lock(&mutex);
}
if (arrayToGetFrom->size() > 0) {
buffer = (*arrayToGetFrom)[0];
arrayToGetFrom->erase(arrayToGetFrom->begin());
} else {
bufferPoolStatsByCapacity[byteCount].allocFallbackCount++;
}
if (isThreadSafe) {
pthread_mutex_unlock(&mutex);
}
if (buffer == nullptr) {
buffer = new NativeByteBuffer(byteCount);
// Аллокации идут сотнями за секунду и полезной информации не несут:
// размеры повторяются, а счётчики пула и так публикуются отдельно.
// Пишем не чаще раза в секунду, указывая, сколько записей свёрнуто.
if (LOGS_ENABLED) {
static int64_t lastAllocLogTime = 0;
static uint32_t suppressedAllocLogs = 0;
int64_t now = ConnectionsManager::getInstance(0).getCurrentTimeMonotonicMillis();
if (now - lastAllocLogTime >= 1000) {
DEBUG_D("create new %u buffer, allocations suppressed=%u", byteCount, suppressedAllocLogs);
lastAllocLogTime = now;
suppressedAllocLogs = 0;
} else {
suppressedAllocLogs++;
}
}
}
}
if (buffer != nullptr) {
buffer->limit(size);
buffer->rewind();
}
return buffer;
}
void BuffersStorage::reuseFreeBuffer(NativeByteBuffer *buffer) {
if (buffer == nullptr) {
return;
}
std::vector<NativeByteBuffer *> *arrayToReuse = nullptr;
uint32_t capacity = buffer->capacity();
uint32_t maxCount = bufferPoolMaxCountForCapacity(capacity);
if (capacity == 8) {
arrayToReuse = &freeBuffers8;
} else if (capacity == 128) {
arrayToReuse = &freeBuffers128;
} else if (capacity == 1024 + 200) {
arrayToReuse = &freeBuffers1024;
} else if (capacity == 4096 + 200) {
arrayToReuse = &freeBuffers4096;
} else if (capacity == 16384 + 200) {
arrayToReuse = &freeBuffers16384;
} else if (capacity == 40000) {
arrayToReuse = &freeBuffers32768;
} else if (capacity == 160000) {
arrayToReuse = &freeBuffersBig;
}
if (arrayToReuse != nullptr) {
if (isThreadSafe) {
pthread_mutex_lock(&mutex);
}
int64_t now = bufferPoolMonotonicMillis();
BufferPoolDebugStats &stats = bufferPoolStatsByCapacity[capacity];
stats.reusedCount++;
if (arrayToReuse->size() < maxCount) {
arrayToReuse->push_back(buffer);
if (arrayToReuse->size() > stats.peakCachedCount) {
stats.peakCachedCount = (uint32_t) arrayToReuse->size();
}
} else {
if (LOGS_ENABLED) {
int64_t lastLogTime = lastPressureLogByCapacity[capacity];
if (lastLogTime == 0 || now - lastLogTime >= BUFFER_POOL_PRESSURE_LOG_INTERVAL_MS) {
lastPressureLogByCapacity[capacity] = now;
DEBUG_D("buffer_pool_pressure size=%u active=%u cap=%u alloc_fallback=%u", capacity, (uint32_t) arrayToReuse->size(), maxCount, stats.allocFallbackCount);
}
}
delete buffer;
}
if (LOGS_ENABLED && (stats.lastSummaryLogTime == 0 || now - stats.lastSummaryLogTime >= BUFFER_POOL_SUMMARY_INTERVAL_MS)) {
stats.lastSummaryLogTime = now;
DEBUG_D("buffer_pool_summary size=%u peak=%u alloc_fallback=%u reused=%u", capacity, stats.peakCachedCount, stats.allocFallbackCount, stats.reusedCount);
}
if (isThreadSafe) {
pthread_mutex_unlock(&mutex);
}
} else {
delete buffer;
}
}