Keys
A record's partition key picks its shard and its order; four places can name it, and a record nobody keyed gets a process-unique one.
The service hashes the key to pick the shard, so every record of one key lands on one shard, and a reader sees them in the order they were written.
A key is 1 to 256 characters; the service refuses a record with any other with KinesisError::Publish.
One key is one shard's write throughput, which the service limits per shard, so a key that every record shares orders them all at the cost of that throughput.
The publisher takes the first key it finds, in this order:
- the
partition_keystep of the publish; - the
partition-keyheader,PARTITION_KEY_HEADER, that the message carries; - the key the mount site named on the policy, with
KinesisPublishSettings; - a key unique to the process, which spreads the records over the shards.
The header is the portable spelling: a body that names the key that way stays free of this crate, and a message that crossed from another broker of the framework carries its key in it.
A message type declares the header in its typed header contract, #[outgoing(headers = ..)], with a field renamed to partition-key; a HeaderMap with that entry works the same.
The key travels in the record's own partition-key field, and the header is left out of the envelope.
On delivery the subscription reports the key under the same header.
Example
Every shipment of one customer carries the customer as its key, so the warehouse reads them in order.
- Macros
- Manual
/// The header a shipment carries: `partition-key`, which becomes the record's partition key.
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct ShipmentKey {
#[serde(rename = "partition-key")]
customer: String,
}
/// Every shipment of one customer lands on one shard, so its consumer sees them in order.
#[derive(Debug, PartialEq, Deserialize, Serialize, Outgoing)]
#[outgoing(name = "shipments", headers = ShipmentKey)]
struct Shipment {
order: u64,
}
#[derive(OutSlot)]
#[publishes(Shipment)]
struct Shipments;
/// The body names no broker: the plain bound moves between brokers untouched.
#[subscriber(KinesisStream::new("orders"))]
async fn ship(order: &Order, Out(shipments): Out<impl Publisher, Shipments>) -> HandlerOutcome {
let key = ShipmentKey {
customer: order.customer.clone(),
};
let shipment = Shipment { order: order.id };
if shipments
.message(&shipment)
.with_headers(&key)
.publish()
.await
.is_err()
{
return HandlerOutcome::retry();
}
HandlerOutcome::ack()
}
/// The header a shipment carries: `partition-key`, which becomes the record's partition key.
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct ShipmentKey {
#[serde(rename = "partition-key")]
customer: String,
}
/// Every shipment of one customer lands on one shard, so its consumer sees them in order.
#[derive(Debug, PartialEq, Deserialize, Serialize, Outgoing)]
#[outgoing(name = "shipments", headers = ShipmentKey)]
struct Shipment {
order: u64,
}
#[derive(OutSlot)]
#[publishes(Shipment)]
struct Shipments;
/// The body names no broker: the plain bound moves between brokers untouched.
struct Ship;
impl<Entry> Handle<Order, (), Outs<(Entry,)>> for Ship
where
Entry: OutEntry<Shipments, Wire: Publisher>,
{
async fn handle(
&self,
order: &Order,
outs: &Outs<(Entry,)>,
_ctx: &mut Context<'_>,
) -> Result<(), HandlerOutcome> {
let key = ShipmentKey {
customer: order.customer.clone(),
};
let shipment = Shipment { order: order.id };
outs.get(Shipments)
.message(&shipment)
.with_headers(&key)
.publish()
.await
.map_err(|_| HandlerOutcome::retry())
}
}