Skip to content

Commit 0188fe4

Browse files
AhmedSolimanclaude
andcommitted
Migrate partition key ranges to KeyRange and extract sharding types
Part of the effort to break the restate-types monolith into composable, focused utility crates. Replace all ~85 uses of std::ops::RangeInclusive<PartitionKey> with the new KeyRange type from restate-util-sharding. Move PartitionId and EqualSizedPartitionPartitioner into restate-util-sharding alongside KeyRange, re-exporting from restate-types for backwards compatibility. The migration is mechanical: .start()/.end() now return u64 by value instead of &u64, .clone() calls are removed (KeyRange is Copy), and construction switches from a..=b syntax to KeyRange::new(a, b). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8ba5af9 commit 0188fe4

77 files changed

Lines changed: 565 additions & 456 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ If these rules conflict with normal behavior, always follow the rules above.
5252
1. Check crates/cli-util/README.md when doing CLI changes to make sure that you are adhering to the CLI style guide.
5353
1. New or deprecated config options must have `/// Since vX.Y.Z` in their doc comment.
5454
1. Use `ByteCount::from(value)` (from `restate_memory`) when displaying byte sizes in errors/logs.
55+
1. Use `KeyRange` (from `restate_sharding`, re-exported via `restate_types::sharding::KeyRange`) instead of `std::ops::RangeInclusive<PartitionKey>` for partition key ranges. `KeyRange` is `Copy`, 16 bytes (vs 24), and has wire-compatible serde/bilrost encoding.
5556

5657

5758
# Validation Before Committing Changes

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/encoding/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ publish = false
1111
restate-workspace-hack = { workspace = true }
1212

1313
restate-encoding-derive = { version = "0.1.0", path = "derive" }
14+
restate-sharding = { workspace = true }
1415

1516
bilrost = { workspace = true }
1617
bytes = { workspace = true }

crates/encoding/src/lib.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ where
9898

9999
impl<V> NetSerde for HashSet<V> where V: NetSerde {}
100100
impl<Idx> NetSerde for RangeInclusive<Idx> where Idx: NetSerde {}
101+
impl NetSerde for restate_sharding::PartitionId {}
101102
impl<T> NetSerde for Arc<T> where T: NetSerde {}
102103
impl<T> NetSerde for Arc<[T]> where T: NetSerde {}
103104
impl<T> NetSerde for Box<T> where T: NetSerde {}
@@ -131,4 +132,49 @@ mod test {
131132

132133
assert_eq!(x.id.0, y.id);
133134
}
135+
136+
/// Validates that `KeyRange`'s general bilrost encoding produces the same
137+
/// wire format as `RangeInclusive<u64>` with `RestateEncoding`. This ensures
138+
/// that `KeyRange` fields can use `#[bilrost(N)]` and remain wire-compatible
139+
/// with the old `#[bilrost(tag(N), encoding(RestateEncoding))] RangeInclusive<u64>`.
140+
#[test]
141+
fn key_range_wire_compat_with_range_inclusive() {
142+
use restate_sharding::KeyRange;
143+
144+
use super::RestateEncoding;
145+
146+
#[derive(Debug, PartialEq, bilrost::Message)]
147+
struct WithKeyRange {
148+
#[bilrost(1)]
149+
range: KeyRange,
150+
}
151+
152+
#[derive(Debug, PartialEq, bilrost::Message)]
153+
struct WithRangeInclusive {
154+
#[bilrost(tag(1), encoding(RestateEncoding))]
155+
range: std::ops::RangeInclusive<u64>,
156+
}
157+
158+
for (start, end) in [(0u64, 0u64), (1, 100), (0, u64::MAX), (42, 42)] {
159+
let kr = KeyRange::new(start, end);
160+
let ri = start..=end;
161+
162+
let kr_bytes = WithKeyRange { range: kr }.encode_to_vec();
163+
let ri_bytes = WithRangeInclusive { range: ri.clone() }.encode_to_vec();
164+
165+
assert_eq!(
166+
kr_bytes, ri_bytes,
167+
"wire format mismatch for range ({start}, {end})"
168+
);
169+
170+
// Cross-decode
171+
let decoded: WithRangeInclusive =
172+
WithRangeInclusive::decode(&*kr_bytes).expect("cross-decode KeyRange→RI");
173+
assert_eq!(decoded.range, ri);
174+
175+
let decoded: WithKeyRange =
176+
WithKeyRange::decode(&*ri_bytes).expect("cross-decode RI→KeyRange");
177+
assert_eq!(decoded.range, kr);
178+
}
179+
}
134180
}

crates/ingestion-client/src/client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ mod test {
440440
let partition = pt.get(&partition_id).unwrap();
441441
client
442442
.ingest(
443-
*partition.key_range.start(),
443+
partition.key_range.start(),
444444
InputRecord::from_str(format!("partition {p}")),
445445
)
446446
.await

crates/invoker-api/src/handle.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,16 @@
88
// the Business Source License, use of this software will be governed
99
// by the Apache License, Version 2.0.
1010

11-
use std::ops::RangeInclusive;
12-
13-
use restate_types::vqueues::VQueueId;
1411
use tokio::sync::mpsc;
1512

1613
use restate_errors::NotRunningError;
1714
use restate_futures_util::concurrency::Permit;
1815
use restate_memory::MemoryLease;
19-
use restate_types::identifiers::{EntryIndex, InvocationId, PartitionKey, PartitionLeaderEpoch};
16+
use restate_types::identifiers::{EntryIndex, InvocationId, PartitionLeaderEpoch};
2017
use restate_types::invocation::InvocationTarget;
2118
use restate_types::journal_v2::{CommandIndex, NotificationId};
19+
use restate_types::sharding::KeyRange;
20+
use restate_types::vqueues::VQueueId;
2221

2322
use super::Effect;
2423

@@ -89,7 +88,7 @@ pub trait InvokerHandle<SR> {
8988
fn register_partition(
9089
&mut self,
9190
partition: PartitionLeaderEpoch,
92-
partition_key_range: RangeInclusive<PartitionKey>,
91+
partition_key_range: KeyRange,
9392
storage_reader: SR,
9493
sender: mpsc::Sender<Box<Effect>>,
9594
) -> Result<(), NotRunningError>;

crates/invoker-api/src/lib.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ pub mod test_util {
2626
use super::*;
2727
use std::convert::Infallible;
2828
use std::marker::PhantomData;
29-
use std::ops::RangeInclusive;
3029

3130
use bytes::Bytes;
3231
use tokio::sync::mpsc::Sender;
@@ -36,10 +35,9 @@ pub mod test_util {
3635
use restate_memory::{
3736
IgnorePinnableMemoryStream, LocalMemoryLease, LocalMemoryPool, MemoryLease,
3837
};
39-
use restate_types::identifiers::{
40-
EntryIndex, InvocationId, PartitionKey, PartitionLeaderEpoch, ServiceId,
41-
};
38+
use restate_types::identifiers::{EntryIndex, InvocationId, PartitionLeaderEpoch, ServiceId};
4239
use restate_types::invocation::{InvocationTarget, ServiceInvocationSpanContext};
40+
use restate_types::sharding::KeyRange;
4341
use restate_types::time::MillisSinceEpoch;
4442
use restate_types::vqueues::VQueueId;
4543

@@ -238,7 +236,7 @@ pub mod test_util {
238236
fn register_partition(
239237
&mut self,
240238
_partition: PartitionLeaderEpoch,
241-
_partition_key_range: RangeInclusive<PartitionKey>,
239+
_partition_key_range: KeyRange,
242240
_storage_reader: SR,
243241
_sender: Sender<Box<Effect>>,
244242
) -> Result<(), NotRunningError> {

crates/invoker-api/src/status_handle.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@
88
// the Business Source License, use of this software will be governed
99
// by the Apache License, Version 2.0.
1010

11+
use std::future::Future;
12+
use std::time::SystemTime;
13+
1114
use codederror::Code;
15+
1216
use restate_types::errors::InvocationError;
13-
use restate_types::identifiers::{DeploymentId, InvocationId, PartitionKey};
17+
use restate_types::identifiers::{DeploymentId, InvocationId};
1418
use restate_types::identifiers::{LeaderEpoch, PartitionId, PartitionLeaderEpoch};
1519
use restate_types::journal::{EntryIndex, EntryType};
1620
use restate_types::service_protocol::ServiceProtocolVersion;
17-
use std::future::Future;
18-
use std::ops::RangeInclusive;
19-
use std::time::SystemTime;
21+
use restate_types::sharding::KeyRange;
2022

2123
// -- Status data structure
2224

@@ -125,10 +127,7 @@ pub trait StatusHandle {
125127
/// filtered by the partition key range
126128
///
127129
/// The data returned by this method is eventually consistent.
128-
fn read_status(
129-
&self,
130-
keys: RangeInclusive<PartitionKey>,
131-
) -> impl Future<Output = Self::Iterator> + Send;
130+
fn read_status(&self, keys: KeyRange) -> impl Future<Output = Self::Iterator> + Send;
132131
}
133132

134133
#[cfg(any(test, feature = "test-util"))]
@@ -148,7 +147,7 @@ pub mod test_util {
148147
impl StatusHandle for MockStatusHandle {
149148
type Iterator = std::vec::IntoIter<InvocationStatusReport>;
150149

151-
async fn read_status(&self, _keys: RangeInclusive<PartitionKey>) -> Self::Iterator {
150+
async fn read_status(&self, _keys: KeyRange) -> Self::Iterator {
152151
self.0.clone().into_iter()
153152
}
154153
}

crates/invoker-impl/src/input_command.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,15 @@
88
// the Business Source License, use of this software will be governed
99
// by the Apache License, Version 2.0.
1010

11-
use std::ops::RangeInclusive;
12-
1311
use tokio::sync::mpsc;
1412

1513
use restate_errors::NotRunningError;
1614
use restate_futures_util::concurrency::Permit;
1715
use restate_memory::MemoryLease;
18-
use restate_types::identifiers::{EntryIndex, InvocationId, PartitionKey, PartitionLeaderEpoch};
16+
use restate_types::identifiers::{EntryIndex, InvocationId, PartitionLeaderEpoch};
1917
use restate_types::invocation::InvocationTarget;
2018
use restate_types::journal_v2::{CommandIndex, NotificationId};
19+
use restate_types::sharding::KeyRange;
2120
use restate_types::vqueues::VQueueId;
2221

2322
use restate_invoker_api::{Effect, InvocationStatusReport, StatusHandle};
@@ -93,7 +92,7 @@ pub(crate) enum InputCommand<SR> {
9392
// needed for dynamic registration at Invoker
9493
RegisterPartition {
9594
partition: PartitionLeaderEpoch,
96-
partition_key_range: RangeInclusive<PartitionKey>,
95+
partition_key_range: KeyRange,
9796
storage_reader: SR,
9897
sender: mpsc::Sender<Box<Effect>>,
9998
},
@@ -241,7 +240,7 @@ impl<SR: Send> restate_invoker_api::InvokerHandle<SR> for InvokerHandle<SR> {
241240
fn register_partition(
242241
&mut self,
243242
partition: PartitionLeaderEpoch,
244-
partition_key_range: RangeInclusive<PartitionKey>,
243+
partition_key_range: KeyRange,
245244
storage_reader: SR,
246245
sender: mpsc::Sender<Box<Effect>>,
247246
) -> Result<(), NotRunningError> {
@@ -259,10 +258,7 @@ impl<SR: Send> restate_invoker_api::InvokerHandle<SR> for InvokerHandle<SR> {
259258
#[derive(Debug, Clone)]
260259
pub struct ChannelStatusReader(
261260
pub(super) mpsc::UnboundedSender<
262-
restate_futures_util::command::Command<
263-
RangeInclusive<PartitionKey>,
264-
Vec<InvocationStatusReport>,
265-
>,
261+
restate_futures_util::command::Command<KeyRange, Vec<InvocationStatusReport>>,
266262
>,
267263
);
268264

@@ -272,7 +268,7 @@ impl StatusHandle for ChannelStatusReader {
272268
std::vec::IntoIter<InvocationStatusReport>,
273269
>;
274270

275-
async fn read_status(&self, keys: RangeInclusive<PartitionKey>) -> Self::Iterator {
271+
async fn read_status(&self, keys: KeyRange) -> Self::Iterator {
276272
let (cmd, rx) = restate_futures_util::command::Command::prepare(keys);
277273
if self.0.send(cmd).is_err() {
278274
return itertools::Either::Left(std::iter::empty::<InvocationStatusReport>());

crates/invoker-impl/src/lib.rs

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ mod status_store;
2020

2121
use std::collections::{HashMap, HashSet};
2222
use std::io::ErrorKind;
23-
use std::ops::RangeInclusive;
23+
use std::ops::RangeBounds;
2424
use std::path::PathBuf;
2525
use std::pin::Pin;
2626
use std::time::{Duration, Instant, SystemTime};
@@ -49,7 +49,7 @@ use restate_service_client::{AssumeRoleCacheMode, ServiceClient};
4949
use restate_time_util::DurationExt;
5050
use restate_types::config::{Configuration, InvokerOptions, ServiceClientOptions};
5151
use restate_types::deployment::PinnedDeployment;
52-
use restate_types::identifiers::{DeploymentId, InvocationId, PartitionKey, WithPartitionKey};
52+
use restate_types::identifiers::{DeploymentId, InvocationId, WithPartitionKey};
5353
use restate_types::identifiers::{PartitionId, PartitionLeaderEpoch};
5454
use restate_types::invocation::InvocationTarget;
5555
use restate_types::journal::EntryIndex;
@@ -61,6 +61,7 @@ use restate_types::journal_v2::{CommandIndex, EntryMetadata, NotificationId};
6161
use restate_types::live::{Live, LiveLoad};
6262
use restate_types::schema::deployment::DeploymentResolver;
6363
use restate_types::schema::invocation_target::InvocationTargetResolver;
64+
use restate_types::sharding::KeyRange;
6465
use tokio_util::time::DelayQueue;
6566
use tokio_util::time::delay_queue::Key as RetryTimerKey;
6667

@@ -203,10 +204,7 @@ pub struct Service<StorageReader, EntryEnricher, Schemas> {
203204
// Used for constructing the invoker sender and status reader
204205
input_tx: mpsc::UnboundedSender<InputCommand<StorageReader>>,
205206
status_tx: mpsc::UnboundedSender<
206-
restate_futures_util::command::Command<
207-
RangeInclusive<PartitionKey>,
208-
Vec<InvocationStatusReport>,
209-
>,
207+
restate_futures_util::command::Command<KeyRange, Vec<InvocationStatusReport>>,
210208
>,
211209
// For the segment queue
212210
tmp_dir: PathBuf,
@@ -386,10 +384,7 @@ where
386384
struct ServiceInner<InvocationTaskRunner, Schemas, StorageReader> {
387385
input_rx: mpsc::UnboundedReceiver<InputCommand<StorageReader>>,
388386
status_rx: mpsc::UnboundedReceiver<
389-
restate_futures_util::command::Command<
390-
RangeInclusive<PartitionKey>,
391-
Vec<InvocationStatusReport>,
392-
>,
387+
restate_futures_util::command::Command<KeyRange, Vec<InvocationStatusReport>>,
393388
>,
394389

395390
// Channel to communicate with invocation tasks
@@ -449,10 +444,10 @@ where
449444

450445
tokio::select! {
451446
Some(cmd) = self.status_rx.recv() => {
452-
let keys = cmd.payload();
447+
let keys = *cmd.payload();
453448
let statuses = self
454449
.invocation_state_machine_manager
455-
.registered_partitions_with_keys(keys.clone())
450+
.registered_partitions_with_keys(keys)
456451
.flat_map(|partition| self.status_store.status_for_partition(partition))
457452
.filter(|status| keys.contains(&status.invocation_id().partition_key()))
458453
.collect();
@@ -607,7 +602,7 @@ where
607602
fn handle_register_partition(
608603
&mut self,
609604
partition: PartitionLeaderEpoch,
610-
partition_key_range: RangeInclusive<PartitionKey>,
605+
partition_key_range: KeyRange,
611606
storage_reader: IR,
612607
sender: mpsc::Sender<Box<Effect>>,
613608
) {
@@ -1850,10 +1845,7 @@ mod tests {
18501845
) -> (
18511846
mpsc::UnboundedSender<InputCommand<IR>>,
18521847
mpsc::UnboundedSender<
1853-
restate_futures_util::command::Command<
1854-
RangeInclusive<PartitionKey>,
1855-
Vec<InvocationStatusReport>,
1856-
>,
1848+
restate_futures_util::command::Command<KeyRange, Vec<InvocationStatusReport>>,
18571849
>,
18581850
Self,
18591851
) {
@@ -1887,7 +1879,7 @@ mod tests {
18871879
let (partition_tx, partition_rx) = mpsc::channel(1024);
18881880
self.handle_register_partition(
18891881
MOCK_PARTITION,
1890-
RangeInclusive::new(0, 0),
1882+
KeyRange::new(0, 0),
18911883
storage_reader,
18921884
partition_tx,
18931885
);
@@ -2139,7 +2131,7 @@ mod tests {
21392131
handle
21402132
.register_partition(
21412133
partition_leader_epoch,
2142-
RangeInclusive::new(0, 0),
2134+
KeyRange::new(0, 0),
21432135
EmptyStorageReader,
21442136
output_tx,
21452137
)

0 commit comments

Comments
 (0)