@@ -199,7 +199,8 @@ impl CreateSequence {
199
199
/// Client encapsulates ZooKeeper session to interact with ZooKeeper cluster.
200
200
///
201
201
/// Besides semantic errors, node operations could also fail due to cluster availability and
202
- /// limitations, e.g. [Error::ConnectionLoss], [Error::QuotaExceeded] and so on.
202
+ /// capabilities, e.g. [Error::ConnectionLoss], [Error::QuotaExceeded], [Error::Unimplemented] and
203
+ /// so on.
203
204
///
204
205
/// All remote operations will fail after session expired, failed or closed.
205
206
///
@@ -211,6 +212,7 @@ impl CreateSequence {
211
212
#[ derive( Clone , Debug ) ]
212
213
pub struct Client {
213
214
chroot : OwnedChroot ,
215
+ version : Version ,
214
216
session : ( SessionId , Vec < u8 > ) ,
215
217
session_timeout : Duration ,
216
218
requester : mpsc:: UnboundedSender < SessionOperation > ,
@@ -232,13 +234,14 @@ impl Client {
232
234
233
235
pub ( crate ) fn new (
234
236
chroot : OwnedChroot ,
237
+ version : Version ,
235
238
session : ( SessionId , Vec < u8 > ) ,
236
239
timeout : Duration ,
237
240
requester : mpsc:: UnboundedSender < SessionOperation > ,
238
241
state_receiver : watch:: Receiver < SessionState > ,
239
242
) -> Client {
240
243
let state_watcher = StateWatcher :: new ( state_receiver) ;
241
- Client { chroot, session, session_timeout : timeout, requester, state_watcher }
244
+ Client { chroot, version , session, session_timeout : timeout, requester, state_watcher }
242
245
}
243
246
244
247
fn validate_path < ' a > ( & ' a self , path : & ' a str ) -> Result < ChrootPath < ' a > > {
@@ -422,6 +425,12 @@ impl Client {
422
425
/// * [Error::NoNode] if parent node does not exist.
423
426
/// * [Error::NoChildrenForEphemerals] if parent node is ephemeral.
424
427
/// * [Error::InvalidAcl] if acl is invalid or empty.
428
+ ///
429
+ /// # Notable behaviors
430
+ /// The resulting [Stat] will be [Stat::is_invalid] if assumed server version is 3.4 series or
431
+ /// below. See [ClientBuilder::assume_server_version] and [ZOOKEEPER-1297][].
432
+ ///
433
+ /// [ZOOKEEPER-1297]: https://issues.apache.org/jira/browse/ZOOKEEPER-1297
425
434
pub fn create < ' a : ' f , ' b : ' f , ' f > (
426
435
& ' a self ,
427
436
path : & ' b str ,
@@ -449,8 +458,10 @@ impl Client {
449
458
OpCode :: CreateTtl
450
459
} else if create_mode. is_container ( ) {
451
460
OpCode :: CreateContainer
452
- } else {
461
+ } else if self . version >= Version ( 3 , 5 , 0 ) {
453
462
OpCode :: Create2
463
+ } else {
464
+ OpCode :: Create
454
465
} ;
455
466
let flags = create_mode. as_flags ( ttl != 0 ) ;
456
467
let request = CreateRequest { path : chroot_path, data, acls : options. acls , flags, ttl } ;
@@ -461,7 +472,8 @@ impl Client {
461
472
let server_path = record:: unmarshal_entity :: < & str > ( & "server path" , & mut buf) ?;
462
473
let client_path = util:: strip_root_path ( server_path, self . chroot . root ( ) ) ?;
463
474
let sequence = if sequential { Self :: parse_sequence ( client_path, path) ? } else { CreateSequence ( -1 ) } ;
464
- let stat = record:: unmarshal :: < Stat > ( & mut buf) ?;
475
+ let stat =
476
+ if op_code == OpCode :: Create { Stat :: new_invalid ( ) } else { record:: unmarshal :: < Stat > ( & mut buf) ? } ;
465
477
Ok ( ( stat, sequence) )
466
478
} )
467
479
}
@@ -1508,10 +1520,14 @@ impl Drop for OwnedLockClient {
1508
1520
}
1509
1521
}
1510
1522
1523
+ #[ derive( Copy , Clone , Debug , PartialEq , PartialOrd ) ]
1524
+ pub ( crate ) struct Version ( u32 , u32 , u32 ) ;
1525
+
1511
1526
/// Builder for [Client] with more options than [Client::connect].
1512
1527
#[ derive( Clone , Debug ) ]
1513
1528
pub struct ClientBuilder {
1514
1529
authes : Vec < AuthPacket > ,
1530
+ version : Version ,
1515
1531
session : Option < ( SessionId , Vec < u8 > ) > ,
1516
1532
readonly : bool ,
1517
1533
detached : bool ,
@@ -1523,6 +1539,7 @@ impl ClientBuilder {
1523
1539
fn new ( ) -> Self {
1524
1540
Self {
1525
1541
authes : Default :: default ( ) ,
1542
+ version : Version ( u32:: MAX , u32:: MAX , u32:: MAX ) ,
1526
1543
session : None ,
1527
1544
readonly : false ,
1528
1545
detached : false ,
@@ -1565,6 +1582,20 @@ impl ClientBuilder {
1565
1582
self
1566
1583
}
1567
1584
1585
+ /// Specifies client assumed server version of ZooKeeper cluster.
1586
+ ///
1587
+ /// Client will issue server compatible protocol to avoid [Error::Unimplemented] for some
1588
+ /// operations. See [Client::create] for an example.
1589
+ ///
1590
+ /// See [ZOOKEEPER-1381][] and [ZOOKEEPER-3762][] for references.
1591
+ ///
1592
+ /// [ZOOKEEPER-1381]: https://issues.apache.org/jira/browse/ZOOKEEPER-1381
1593
+ /// [ZOOKEEPER-3762]: https://issues.apache.org/jira/browse/ZOOKEEPER-3762
1594
+ pub fn assume_server_version ( & mut self , major : u32 , minor : u32 , patch : u32 ) -> & mut Self {
1595
+ self . version = Version ( major, minor, patch) ;
1596
+ self
1597
+ }
1598
+
1568
1599
/// Detaches creating session so it will not be closed after all client instances dropped.
1569
1600
pub fn detach ( & mut self ) -> & mut Self {
1570
1601
self . detached = true ;
@@ -1608,7 +1639,8 @@ impl ClientBuilder {
1608
1639
tokio:: spawn ( async move {
1609
1640
session. serve ( servers, sock, buf, connecting_depot, receiver) . await ;
1610
1641
} ) ;
1611
- let client = Client :: new ( chroot. to_owned ( ) , session_info, session_timeout, sender, state_receiver) ;
1642
+ let client =
1643
+ Client :: new ( chroot. to_owned ( ) , self . version , session_info, session_timeout, sender, state_receiver) ;
1612
1644
Ok ( client)
1613
1645
}
1614
1646
}
0 commit comments