Repository: tikv/tikv
File: components/raftstore/src/store/entry_storage.rs (Rust)

Code before:
        mem_size_change += self.shrink_if_necessary();
        self.flush_mem_size_change(mem_size_change);
    }

    fn append_impl(&mut self, region_id: u64, peer_id: u64, entries: &[Entry]) -> i64 {
        let mut mem_size_change = 0;

        if let Some(cache_last_index) = self.cache.back().map(|e| e.get_index()) {
            let first_index = entries[0].get_index();
            if cache_last_index >= first_index {
                let cache_len = self.cache.len();
                let truncate_to = cache_len
                    .checked_sub((cache_last_index - first_index + 1) as usize)
                    .unwrap_or_default();
                let trunc_to_idx = self.cache[truncate_to].index;
                for e in self.cache.drain(truncate_to..) {
                    mem_size_change -=
                        (bytes_capacity(&e.data) + bytes_capacity(&e.context)) as i64;
                }
                if let Some(cached) = self.trace.back() {
                    // Only committed entries can be traced, and only uncommitted entries
                    // can be truncated. So there won't be any overlaps.
                    let cached_last = cached.range.end - 1;
                    // TODO: if we are going to support apply unpersisted entries on
                    // follower, this assert will not longer be correct.
                    assert!(cached_last < trunc_to_idx);
                }
            } else if cache_last_index + 1 < first_index {
                panic!(
                    "[region {}] {} unexpected hole: {} < {}",
                    region_id, peer_id, cache_last_index, first_index
                );
            }
        }

        for e in entries {
            self.cache.push_back(e.to_owned());
            mem_size_change += (bytes_capacity(&e.data) + bytes_capacity(&e.context)) as i64;
        }
        // In the past, the entry cache will be truncated if its size exceeds a certain
        // number. However, after introducing async write io, the entry must stay in
        // cache if it's not persisted to raft db because the raft-rs may need to read
        // entries.(e.g. leader sends MsgAppend to followers)

        mem_size_change
    }

    pub fn entry(&self, idx: u64) -> Option<&Entry> {
        let cache_low = self.cache.front()?.get_index();
        if idx >= cache_low {
            Some(&self.cache[(idx - cache_low) as usize])

Review comment (performance):
```suggestion
                    assert!(cached_last < trunc_to_idx, "cached_last: {}, trunc_to_idx: {}", cached_last, trunc_to_idx);
```

Code after:
        mem_size_change += self.shrink_if_necessary();
        self.flush_mem_size_change(mem_size_change);
    }

    fn append_impl(&mut self, region_id: u64, peer_id: u64, entries: &[Entry]) -> i64 {
        let mut mem_size_change = 0;

        if let Some(cache_last_index) = self.cache.back().map(|e| e.get_index()) {
            let first_index = entries[0].get_index();
            if cache_last_index >= first_index {
                let cache_len = self.cache.len();
                let truncate_to = cache_len
                    .checked_sub((cache_last_index - first_index + 1) as usize)
                    .unwrap_or_default();
                let trunc_to_idx = self.cache[truncate_to].index;
                for e in self.cache.drain(truncate_to..) {
                    mem_size_change -=
                        (bytes_capacity(&e.data) + bytes_capacity(&e.context)) as i64;
                }
                if let Some(cached) = self.trace.back() {
                    // Only committed entries can be traced, and only uncommitted entries
                    // can be truncated. So there won't be any overlaps.
                    let cached_last = cached.range.end - 1;
                    // TODO: if we are going to support apply unpersisted entries on
                    // follower, this assert will not longer be correct.
                    assert!(
                        cached_last < trunc_to_idx,
                        "cached_last: {}, trunc_to_idx: {}",
                        cached_last,
                        trunc_to_idx
                    );
                }
            } else if cache_last_index + 1 < first_index {
                panic!(
                    "[region {}] {} unexpected hole: {} < {}",
                    region_id, peer_id, cache_last_index, first_index
                );
            }
        }

        for e in entries {
            self.cache.push_back(e.to_owned());
            mem_size_change += (bytes_capacity(&e.data) + bytes_capacity(&e.context)) as i64;
        }
        // In the past, the entry cache will be truncated if its size exceeds a certain
        // number. However, after introducing async write io, the entry must stay in
        // cache if it's not persisted to raft db because the raft-rs may need to read
        // entries.(e.g. leader sends MsgAppend to followers)

        mem_size_change
    }