|
2 | 2 |
|
3 | 3 | use crate::{ |
4 | 4 | backoff::Backoff, |
| 5 | + kv::HasKeys, |
5 | 6 | pd::PdClient, |
6 | 7 | request::{KvRequest, Shardable}, |
7 | 8 | stats::tikv_stats, |
8 | 9 | transaction::{resolve_locks, HasLocks}, |
9 | | - Error, Result, |
| 10 | + util::iter::FlatMapOkIterExt, |
| 11 | + Error, Key, KvPair, Result, Value, |
10 | 12 | }; |
11 | 13 | use async_trait::async_trait; |
12 | 14 | use futures::{prelude::*, stream::StreamExt}; |
13 | 15 | use std::{marker::PhantomData, sync::Arc}; |
| 16 | +use tikv_client_proto::kvrpcpb; |
14 | 17 | use tikv_client_store::{HasError, HasRegionError, KvClient}; |
15 | 18 |
|
16 | 19 | /// A plan for how to execute a request. A user builds up a plan with various |
@@ -55,6 +58,12 @@ impl<Req: KvRequest> Plan for Dispatch<Req> { |
55 | 58 | } |
56 | 59 | } |
57 | 60 |
|
| 61 | +impl<Req: KvRequest + HasKeys> HasKeys for Dispatch<Req> { |
| 62 | + fn get_keys(&self) -> Vec<Key> { |
| 63 | + self.request.get_keys() |
| 64 | + } |
| 65 | +} |
| 66 | + |
58 | 67 | pub struct MultiRegion<P: Plan, PdC: PdClient> { |
59 | 68 | pub(super) inner: P, |
60 | 69 | pub pd_client: Arc<PdC>, |
@@ -123,6 +132,9 @@ impl<In: Clone + Send + Sync + 'static, P: Plan<Result = Vec<Result<In>>>, M: Me |
123 | 132 | #[derive(Clone, Copy)] |
124 | 133 | pub struct Collect; |
125 | 134 |
|
| 135 | +#[derive(Clone, Debug)] |
| 136 | +pub struct CollectAndMatchKey; |
| 137 | + |
126 | 138 | /// A merge strategy which returns an error if any response is an error and |
127 | 139 | /// otherwise returns a Vec of the results. |
128 | 140 | #[derive(Clone, Copy)] |
@@ -256,6 +268,12 @@ where |
256 | 268 | } |
257 | 269 | } |
258 | 270 |
|
| 271 | +impl<P: Plan + HasKeys, PdC: PdClient> HasKeys for ResolveLock<P, PdC> { |
| 272 | + fn get_keys(&self) -> Vec<Key> { |
| 273 | + self.inner.get_keys() |
| 274 | + } |
| 275 | +} |
| 276 | + |
259 | 277 | pub struct ExtractError<P: Plan> { |
260 | 278 | pub inner: P, |
261 | 279 | } |
@@ -292,6 +310,34 @@ where |
292 | 310 | } |
293 | 311 | } |
294 | 312 |
|
| 313 | +// Requires: len(inner.keys) == len(inner.result) |
| 314 | +pub struct PreserveKey<P: Plan + HasKeys> { |
| 315 | + pub inner: P, |
| 316 | +} |
| 317 | + |
| 318 | +impl<P: Plan + HasKeys> Clone for PreserveKey<P> { |
| 319 | + fn clone(&self) -> Self { |
| 320 | + PreserveKey { |
| 321 | + inner: self.inner.clone(), |
| 322 | + } |
| 323 | + } |
| 324 | +} |
| 325 | + |
| 326 | +#[async_trait] |
| 327 | +impl<P> Plan for PreserveKey<P> |
| 328 | +where |
| 329 | + P: Plan + HasKeys, |
| 330 | +{ |
| 331 | + type Result = ResponseAndKeys<P::Result>; |
| 332 | + |
| 333 | + async fn execute(&self) -> Result<Self::Result> { |
| 334 | + let keys = self.inner.get_keys(); |
| 335 | + let res = self.inner.execute().await?; |
| 336 | + // TODO: should we check they have the same length? |
| 337 | + Ok(ResponseAndKeys(res, keys)) |
| 338 | + } |
| 339 | +} |
| 340 | + |
295 | 341 | #[cfg(test)] |
296 | 342 | mod test { |
297 | 343 | use super::*; |
@@ -349,3 +395,60 @@ mod test { |
349 | 395 | .for_each(|r| assert!(r.is_err())); |
350 | 396 | } |
351 | 397 | } |
| 398 | + |
| 399 | +// contains a response and the corresponding keys |
| 400 | +// currently only used for matching keys and values in pessimistic lock requests |
| 401 | +#[derive(Debug, Clone)] |
| 402 | +pub struct ResponseAndKeys<Resp>(Resp, Vec<Key>); |
| 403 | + |
| 404 | +impl<Resp: HasError> HasError for ResponseAndKeys<Resp> { |
| 405 | + fn error(&mut self) -> Option<Error> { |
| 406 | + self.0.error() |
| 407 | + } |
| 408 | +} |
| 409 | + |
| 410 | +impl<Resp: HasLocks> HasLocks for ResponseAndKeys<Resp> { |
| 411 | + fn take_locks(&mut self) -> Vec<tikv_client_proto::kvrpcpb::LockInfo> { |
| 412 | + self.0.take_locks() |
| 413 | + } |
| 414 | +} |
| 415 | + |
| 416 | +impl<Resp: HasRegionError> HasRegionError for ResponseAndKeys<Resp> { |
| 417 | + fn region_error(&mut self) -> Option<Error> { |
| 418 | + self.0.region_error() |
| 419 | + } |
| 420 | +} |
| 421 | + |
| 422 | +impl Merge<ResponseAndKeys<kvrpcpb::PessimisticLockResponse>> for CollectAndMatchKey { |
| 423 | + type Out = Vec<KvPair>; |
| 424 | + |
| 425 | + fn merge( |
| 426 | + &self, |
| 427 | + input: Vec<Result<ResponseAndKeys<kvrpcpb::PessimisticLockResponse>>>, |
| 428 | + ) -> Result<Self::Out> { |
| 429 | + input |
| 430 | + .into_iter() |
| 431 | + .flat_map_ok(|ResponseAndKeys(mut resp, keys)| { |
| 432 | + let values = resp.take_values(); |
| 433 | + let not_founds = resp.take_not_founds(); |
| 434 | + let v: Vec<_> = if not_founds.is_empty() { |
| 435 | + // Legacy TiKV does not distiguish not existing key and existing key |
| 436 | + // that with empty value. We assume that key does not exist if value |
| 437 | + // is empty. |
| 438 | + let values: Vec<Value> = values.into_iter().filter(|v| v.is_empty()).collect(); |
| 439 | + keys.into_iter().zip(values).map(From::from).collect() |
| 440 | + } else { |
| 441 | + assert_eq!(values.len(), not_founds.len()); |
| 442 | + let values: Vec<Value> = values |
| 443 | + .into_iter() |
| 444 | + .zip(not_founds.into_iter()) |
| 445 | + .filter_map(|(v, not_found)| if not_found { None } else { Some(v) }) |
| 446 | + .collect(); |
| 447 | + keys.into_iter().zip(values).map(From::from).collect() |
| 448 | + }; |
| 449 | + // FIXME sucks to collect and re-iterate, but the iterators have different types |
| 450 | + v.into_iter() |
| 451 | + }) |
| 452 | + .collect() |
| 453 | + } |
| 454 | +} |
0 commit comments