Существуют ли в Rust какие-либо стратегии/рекомендации по обработке редко обновляемых, совместно используемых и изменяемых значений?
struct SharedMutableResource {
some_value: String,
// time at which value must be refreshed
// probably daily or weekly
expires_at: chrono::DateTime<chrono::Utc>,
}
impl SharedMutableResource {
pub fn get_some_value(&mut self) -> &str {
if self.expires_at < Utc::now() {
// some refresh method
self.some_value = some_new_value;
}
self.some_value.as_str()
}
}
struct AsyncRequiresResource {
resource: Arc<SharedMutableResource>,
}
impl AsyncRequiresResource {
pub fn borrow_resource(&self) -> Value {
// is there any way to avoid borrowing as mut here for every request
let some_value = self.resource.get_mut().get_some_value();
// do something with the value
}
}
Я пытаюсь создать реализацию ротации ключей для подписи моих JWT, в идеале меняя ключи от ежедневного до еженедельного. Эти запросы будут поступать асинхронно и должны получить доступ к значению ключа для подписи и проверки токенов. Однако ключи будут чередоваться в зависимости от временных ограничений, поэтому их можно будет обновить по любому запросу.
Помня о безопасности потоков, прямо сейчас я пытаюсь найти оптимальный способ обработки доступа к ключу. В идеале он должен храниться в памяти в стеке во время выполнения, так как я понимаю, что это самый быстрый метод извлечения (для большинства запросов потребуется JWT, и поэтому я хотел бы убедиться, что во время этого процесса я избегаю как можно больше замедлений, таких как подключение к db или операции ввода-вывода). Однако, поскольку это должно быть потокобезопасным, у меня есть две идеи, но ни одна из них не кажется очень хорошей.
Используйте асинхронные дуги (Arc<Mutex> вместо Arc)
Плюсы
Минусы
Используйте асинхронный поток (thread::spawn(task))
Плюсы
Минусы
По сути, оба способа кажутся мне довольно расточительными для изменений, которые случаются очень редко. Я бы не беспокоился о дополнительных накладных расходах для фактического изменения значения, поскольку это происходит так нечасто, но для того, чтобы поддерживать высокие скорости чтения в рамках моих (по общему признанию, наложенных мной) ограничений, мне трудно найти путь вперед. Есть ли какие-либо инструменты, стратегии или какие-то ограничения, которые мне нужно отбросить / иметь неправильное понимание, чтобы разрешить нечасто изменяемые данные с высокой скоростью чтения?
@PitaJ копирование ключа в статический вызовет случайные «полунеправильные» ключи из-за того, что операции копирования не являются атомарными, не так ли?
Нет, статика RwLock
не позволит этому случиться.
То, что я предлагаю в своем ответе, arc-swap
, семантически эквивалентно RwLock<Arc>
, только быстрее.
@PitaJ о, хорошо, я думал, ты предложил это как альтернативу, а не поверх RwLock.
@ChayimFriedman Я изо всех сил пытаюсь понять, зачем нужен Arc
в случае static RwLock
, но arc-swap
, вероятно, все же предпочтительнее, поскольку запросы никогда не будут заблокированы
@PitaJ Обычно в этом нет необходимости, просто более прямая замена ArcSwap
. Хотя удержание блокировки только на время клонирования Arc
происходит быстрее.
Лучше всего использовать ящик arc-swap. Он предназначен именно для этого сценария: данные, которые обычно используются, но редко обновляются. Загрузка данных осуществляется без блокировки, и ящик используется очень часто (25 миллионов загрузок на момент написания статьи на crates.io).
Он предоставляет различные варианты, но самый простой — это ArcSwap.
Что у вас есть на данный момент? Похоже,
RwLock
, вероятно, будет лучше, чемMutex
для вашего случая. Почему бы не поставить это вstatic
?