refactor: убрать ограничение maxChars из хранилища соответствий

- Удалены поле maxChars, параметр конструктора и метод evictWhileOverLimit.
- Хранилище теперь ограничено только TTL (ttl-minutes), без вытеснения по объёму.
- Конструкторы переведены на (int ttlMinutes) и (int ttlMinutes, SharedIndex, PayloadCipher).
- Обновлены тесты и PipelineWarmup на новые сигнатуры.
This commit is contained in:
dakocha3
2026-09-23 22:33:54 +03:00
parent 1328a7b234
commit 3ba248b8a9
28 changed files with 49 additions and 82 deletions
+11 -25
View File
@@ -24,9 +24,8 @@ import org.springframework.stereotype.Component;
* было бы получить в ответ исходные значения из запроса другого потребителя. Разделение
* ограничивает это пределами одной системы, которая и так видит свои данные.
*
* <p>Хранилище ограничено по суммарному объёму строк, а записи живут ограниченное время:
* персональные данные не должны залёживаться в памяти, а крупные тексты не должны исчерпать кучу.
* Вытеснение идёт в порядке добавления и выполняется прямо на записи — отдельного потока и внешней
* <p>Записи живут ограниченное время: персональные данные не должны залёживаться в памяти.
* Протухшие записи убираются в порядке добавления прямо на записи — отдельного потока и внешней
* библиотеки кеширования не требуется.
*
* <p>Когда включён общий слой ({@link SharedIndex}), соответствие пишется ещё и туда, а чтение при
@@ -58,26 +57,30 @@ public class PayloadStore {
private final ConcurrentLinkedQueue<String> insertionOrder = new ConcurrentLinkedQueue<>();
private final AtomicLong charsHeld = new AtomicLong();
private final long maxChars;
private final long ttlMillis;
private final SharedIndex shared;
private final PayloadCipher cipher;
@Autowired
public PayloadStore(
@Value("${pdguard.store.max-chars:134217728}") long maxChars,
@Value("${pdguard.store.ttl-minutes:30}") int ttlMinutes,
SharedIndex shared,
PayloadCipher cipher) {
this.maxChars = maxChars;
this.ttlMillis = ttlMinutes * 60_000L;
this.shared = shared;
this.cipher = cipher;
}
/** Конструктор для тестов: только локальная память, общий слой и шифрование выключены. */
public PayloadStore(long maxChars, int ttlMinutes) {
this(maxChars, ttlMinutes, SharedIndex.disabled(), PayloadCipher.disabled());
public PayloadStore(int ttlMinutes) {
this(ttlMinutes, SharedIndex.disabled(), PayloadCipher.disabled());
}
/** Конструктор для тестов с явным общим слоем и шифрованием. */
public PayloadStore(int ttlMinutes, SharedIndex shared, PayloadCipher cipher) {
this.ttlMillis = ttlMinutes * 60_000L;
this.shared = shared;
this.cipher = cipher;
}
public void put(String system, String payloadId, String original, String masked) {
@@ -92,7 +95,6 @@ public class PayloadStore {
charsHeld.addAndGet((long) entry.weight() - (replaced == null ? 0 : replaced.weight()));
sweepExpired(now);
evictWhileOverLimit();
shared.put(system, payloadId, encrypted, masked, entry.fingerprint());
}
@@ -154,22 +156,6 @@ public class PayloadStore {
}
}
private void evictWhileOverLimit() {
while (charsHeld.get() > maxChars) {
String oldest = insertionOrder.poll();
if (oldest == null) {
return;
}
Entry entry = byId.get(oldest);
if (entry != null) {
// ponytail: если тот же payload_id записали повторно, в очереди остался
// старый след и здесь вытесняется свежая запись. Цена — одно лишнее
// обращение к маскированию; точный учёт потребовал бы двусвязного списка.
forget(oldest, entry);
}
}
}
private void forget(String idKey, Entry entry) {
if (byId.remove(idKey, entry)) {
byMaskFingerprint.remove(ScopedKey.of(entry.system(), entry.fingerprint()), entry);
@@ -62,7 +62,7 @@ public class PipelineWarmup {
}
long started = System.nanoTime();
Pipeline scratch =
new Pipeline(registry, masker, new PayloadStore(1_000_000L, 1), NameCascade.disabled());
new Pipeline(registry, masker, new PayloadStore(1), NameCascade.disabled());
SystemPolicy policy =
new SystemPolicy(
SystemPolicy.DEFAULT_NAME,
-6
View File
@@ -37,12 +37,6 @@ pdguard:
systems-file: config/systems.json
store:
backend: memory
# 128MB (было) держал store.chars упёртым в потолок под держащей нагрузкой —
# FIFO-вытеснение выкидывало свежую запись за миллисекунды, раньше, чем
# приходил её собственный демаскирующий запрос: см. k6 на датасете утечек,
# 6.1% неверных демасков при 2000 VU, при этом 0% на низкой конкурентности.
# На одном узле без соседей по хосту память есть — 512MB даёт запас на порядок.
max-chars: 536870912
ttl-minutes: 30
# 32 байта в hex; AES-256 ключ шифрования хранилища
encryption-key: "46a38b200c6df557a5fd2c8a57ad3fec6b710b9f3e1fef1451d121a094f63573"