У меня есть LoadingCache
, который загружает Netty ByteBuf
из некоторых данных. Мне нужно иметь возможность опубликовать эти данные с помощью release()
после удаления записи, однако существует состояние гонки, при котором запись удаляется до того, как я смогу retain()
это сделать, и возвращаемый ByteBuf
недействителен.
Вот пример того, что я пытаюсь сделать: гонка происходит, когда между получением и сохранением вызывается прослушиватель удаления.
LoadingCache<String, ByteBuf> cache = Caffeine.newBuilder()
.maximumSize(128)
.evictionListener(
(RemovalListener<String, ByteBuf>) (string, buf, removalCause) -> {
buf.release();
}
)
.build(
key -> {
ByteBuf byteBuf = null;
// TODO: Create the ByteBuf from a pool
return byteBuf;
}
);
ByteBuf buffer = cache.get("hello").retain();
// If the entry is evicted between call to get and the retain then a race condition occurs
// That means the reference count drops to 0 before the retain is invoked
Есть ли какой-нибудь способ безопасно и атомарно вызвать сохранение Caffeine перед возвращением из режима получения?
@LouisWasserman PooledByteBufAllocator
по умолчанию предоставляет ByteBuf
со счетчиком ссылок 1, поэтому сохранение его может привести к утечке памяти.
Вы можете использовать asMap().compute
для выполнения операции чтения/записи записи.
Cache<String, ByteBuf> cache = Caffeine.newBuilder()
.evictionListener((String string, ByteBuf buf, RemovalCause cause) -> buf.release())
.maximumSize(128)
.build();
ByteBuf buffer = cache.asMap().compute(key, (k, buf) -> {
if (buf == null) {
buf = // TODO: Create the ByteBuf from a pool
}
buf.retain();
return buf;
});
Вас также может заинтересовать закрепление, где вы помечаете запись как не подлежащую вытеснению, указав, что она потребляет нулевую емкость, поэтому она будет пропущена при вытеснении размера.
Cache<String, ByteBuf> cache = Caffeine.newBuilder()
.weigher((String string, ByteBuf buf) -> (buf.refCnt() == 0) ? 1 : 0)
.maximumWeight(128)
.build();
public ByteBuf acquire(String key) {
// above
}
public void release(String key) {
cache.asMap().compute(key, (k, buf) -> {
buf.release();
return buf;
});
}
Спасибо - это отличное решение. Возможно ли освобождение буфера во время вычислительного вызова или ключ будет временно заблокирован? Например. Вычисление вызывается, буфер не имеет значения NULL, но затем освобождается сразу после проверки условного значения NULL.
Запись будет заблокирована на карте, поэтому другие записи будут заблокированы (в тот же хэшбин). Вычисления являются атомарными, поэтому существует небольшой штраф по сравнению с чтением без блокировки, но в целом все должно быть в порядке.
Вам необходимо сохранить в организме
load
.