blob: 1a4169daf7430348640f9ef47eaca4648bf95a82 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use iggy_common::{IggyMessagesBatchMut, IggyMessagesBatchSet};
use journal::{Journal, Storage};
use std::cell::UnsafeCell;
// TODO: Fix that, we need to figure out how to store the `IggyMessagesBatchSet`.
/// No-op storage backend for the in-memory partition journal.
#[derive(Debug)]
pub struct Noop;
impl Storage for Noop {
type Buffer = ();
async fn write(&self, _buf: ()) -> usize {
0
}
async fn read(&self, _offset: usize, buffer: ()) -> () {
buffer
}
}
/// Lookup key for querying messages from the journal.
#[derive(Debug, Clone, Copy)]
pub enum MessageLookup {
Offset { offset: u64, count: u32 },
Timestamp { timestamp: u64, count: u32 },
}
impl std::ops::Deref for MessageLookup {
type Target = Self;
fn deref(&self) -> &Self {
self
}
}
/// In-memory journal that accumulates message batches as an `IggyMessagesBatchSet`.
///
/// This is a pure storage layer — it holds batches and supports lookups via
/// `MessageLookup`. All tracking metadata (offsets, timestamps, counts) lives
/// outside the journal in the `SegmentedLog`'s `JournalInfo`.
///
/// Uses `UnsafeCell` for interior mutability, matching the single-threaded
/// per-shard execution model.
pub struct PartitionJournal {
batch_set: UnsafeCell<IggyMessagesBatchSet>,
}
impl PartitionJournal {
pub fn new() -> Self {
Self {
batch_set: UnsafeCell::new(IggyMessagesBatchSet::empty()),
}
}
/// Drain all accumulated batches, returning the batch set.
pub fn commit(&self) -> IggyMessagesBatchSet {
let batch_set = unsafe { &mut *self.batch_set.get() };
std::mem::take(batch_set)
}
pub fn is_empty(&self) -> bool {
let batch_set = unsafe { &*self.batch_set.get() };
batch_set.is_empty()
}
}
impl Default for PartitionJournal {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for PartitionJournal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PartitionJournal").finish()
}
}
impl Journal<Noop> for PartitionJournal {
type Header = MessageLookup;
type Entry = IggyMessagesBatchMut;
type HeaderRef<'a> = MessageLookup;
fn header(&self, _idx: usize) -> Option<Self::HeaderRef<'_>> {
unreachable!("fn header: header lookup not supported for partition journal.");
}
fn previous_header(&self, _header: &Self::Header) -> Option<Self::HeaderRef<'_>> {
unreachable!("fn previous_header: header lookup not supported for partition journal.");
}
async fn append(&self, entry: Self::Entry) {
let batch_set = unsafe { &mut *self.batch_set.get() };
batch_set.add_batch(entry);
}
async fn entry(&self, header: &Self::Header) -> Option<Self::Entry> {
// Entry lookups go through SegmentedLog which uses JournalInfo
// to construct MessageLookup headers. The actual query is done
// via get() below, not through the Journal trait.
let _ = header;
unreachable!("fn entry: use SegmentedLog::get() instead for partition journal lookups.");
}
}
impl PartitionJournal {
/// Query messages by offset or timestamp with count.
///
/// This is called by `SegmentedLog` using `MessageLookup` headers
/// constructed from `JournalInfo`.
pub fn get(&self, header: &MessageLookup) -> Option<IggyMessagesBatchSet> {
let batch_set = unsafe { &*self.batch_set.get() };
let result = match header {
MessageLookup::Offset { offset, count } => batch_set.get_by_offset(*offset, *count),
MessageLookup::Timestamp { timestamp, count } => {
batch_set.get_by_timestamp(*timestamp, *count)
}
};
if result.is_empty() {
None
} else {
Some(result)
}
}
}