Repository: kudu
Updated Branches:
  refs/heads/master 56aec48db -> f5021e061


KUDU-687: expose additional tablet metadata in C++ client

This patch adds new data-only KuduReplica and KuduTablet classes.
Along with KuduTabletServer, the C++ client now has more parity with the
Java client w.r.t. exposing tablet metadata.

For now, this is only exposed via KuduScanToken, because it's already doing
the work to figure out which replicas exist where. That's an odd fit for
ksck (which doesn't scan, at least not using the client), but it should
stave off any controversy stemming from adding dubious public APIs. In the
future, it wouldn't be unreasonable for these classes to be exposed via
KuduTable in some way.

There are four public signature changes:
- Addition of KuduScanToken::tablet(): this is backwards compatible.
- Addition of KuduTabletServer::port(): this is backwards compatible.
- Change to the KuduScanToken constructor: this should be backwards
  compatible, because it's private and so shouldn't used by applications.
- Removal of KuduScanToken::TabletServers(): this is an incompatible change.
  I think it's OK because Impala is the only significant C++ client user and
  it's not even using scan tokens yet.

Change-Id: I3cbbb1df8d3adf60425541b57e68595bbf6e92ff
Reviewed-on: http://gerrit.cloudera.org:8080/4146
Tested-by: Kudu Jenkins
Reviewed-by: Todd Lipcon <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/6a92db4d
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/6a92db4d
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/6a92db4d

Branch: refs/heads/master
Commit: 6a92db4d08a635238c866df8ee6198f590347cda
Parents: 56aec48
Author: Adar Dembo <[email protected]>
Authored: Fri Aug 26 19:25:56 2016 -0700
Committer: Todd Lipcon <[email protected]>
Committed: Mon Aug 29 21:18:24 2016 +0000

----------------------------------------------------------------------
 docs/release_notes.adoc                   |   3 +
 src/kudu/client/CMakeLists.txt            |   2 +
 src/kudu/client/client.cc                 |  78 ++++++++++++---
 src/kudu/client/client.h                  | 131 +++++++++++++++++--------
 src/kudu/client/meta_cache.cc             |  14 ++-
 src/kudu/client/meta_cache.h              |   5 +-
 src/kudu/client/replica-internal.cc       |  38 +++++++
 src/kudu/client/replica-internal.h        |  42 ++++++++
 src/kudu/client/scan_token-internal.cc    |  63 ++++++++----
 src/kudu/client/scan_token-internal.h     |  16 +--
 src/kudu/client/scan_token-test.cc        |  49 +++++++--
 src/kudu/client/tablet-internal.cc        |  42 ++++++++
 src/kudu/client/tablet-internal.h         |  42 ++++++++
 src/kudu/client/tablet_server-internal.cc |   8 +-
 src/kudu/client/tablet_server-internal.h  |   5 +-
 15 files changed, 444 insertions(+), 94 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/docs/release_notes.adoc
----------------------------------------------------------------------
diff --git a/docs/release_notes.adoc b/docs/release_notes.adoc
index b629a2f..4060bd6 100644
--- a/docs/release_notes.adoc
+++ b/docs/release_notes.adoc
@@ -49,6 +49,9 @@ detailed below.
 - KuduSession methods in the C++ library are no longer advertised as 
thread-safe
   to have one set of semantics for both C++ and Java Kudu client libraries.
 
+- The KuduScanToken::TabletServers method in the C++ library has been removed.
+  The same information can now be found in the KuduScanToken::tablet method.
+
 [[rn_0.10.0]]
 == Release notes specific to 0.10.0
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/client/CMakeLists.txt b/src/kudu/client/CMakeLists.txt
index eb38862..d0ebf12 100644
--- a/src/kudu/client/CMakeLists.txt
+++ b/src/kudu/client/CMakeLists.txt
@@ -40,12 +40,14 @@ set(CLIENT_SRCS
   scan_predicate.cc
   scan_token-internal.cc
   scanner-internal.cc
+  replica-internal.cc
   resource_metrics.cc
   schema.cc
   session-internal.cc
   table-internal.cc
   table_alterer-internal.cc
   table_creator-internal.cc
+  tablet-internal.cc
   tablet_server-internal.cc
   value.cc
   write_op.cc

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/client.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/client.cc b/src/kudu/client/client.cc
index e7275a0..739c536 100644
--- a/src/kudu/client/client.cc
+++ b/src/kudu/client/client.cc
@@ -34,6 +34,7 @@
 #include "kudu/client/error-internal.h"
 #include "kudu/client/error_collector.h"
 #include "kudu/client/meta_cache.h"
+#include "kudu/client/replica-internal.h"
 #include "kudu/client/row_result.h"
 #include "kudu/client/scan_predicate-internal.h"
 #include "kudu/client/scan_token-internal.h"
@@ -43,6 +44,7 @@
 #include "kudu/client/table-internal.h"
 #include "kudu/client/table_alterer-internal.h"
 #include "kudu/client/table_creator-internal.h"
+#include "kudu/client/tablet-internal.h"
 #include "kudu/client/tablet_server-internal.h"
 #include "kudu/client/write_op.h"
 #include "kudu/common/common.pb.h"
@@ -313,10 +315,12 @@ Status 
KuduClient::ListTabletServers(vector<KuduTabletServer*>* tablet_servers)
   }
   for (int i = 0; i < resp.servers_size(); i++) {
     const ListTabletServersResponsePB_Entry& e = resp.servers(i);
-    auto ts = new KuduTabletServer();
+    HostPort hp;
+    RETURN_NOT_OK(HostPortFromPB(e.registration().rpc_addresses(0), &hp));
+    unique_ptr<KuduTabletServer> ts(new KuduTabletServer);
     ts->data_ = new KuduTabletServer::Data(e.instance_id().permanent_uuid(),
-                                           
e.registration().rpc_addresses(0).host());
-    tablet_servers->push_back(ts);
+                                           hp);
+    tablet_servers->push_back(ts.release());
   }
   return Status::OK();
 }
@@ -1305,9 +1309,10 @@ Status KuduScanner::GetCurrentServer(KuduTabletServer** 
server) {
     return Status::IllegalState(strings::Substitute("No HostPort found for 
RemoteTabletServer $0",
                                                     rts->ToString()));
   }
-  *server = new KuduTabletServer();
-  (*server)->data_ = new KuduTabletServer::Data(rts->permanent_uuid(),
-                                                host_ports[0].host());
+  unique_ptr<KuduTabletServer> client_server(new KuduTabletServer);
+  client_server->data_ = new KuduTabletServer::Data(rts->permanent_uuid(),
+                                                    host_ports[0]);
+  *server = client_server.release();
   return Status::OK();
 }
 
@@ -1315,8 +1320,8 @@ Status KuduScanner::GetCurrentServer(KuduTabletServer** 
server) {
 // KuduScanToken
 ////////////////////////////////////////////////////////////
 
-KuduScanToken::KuduScanToken(KuduScanToken::Data* data)
-    : data_(data) {
+KuduScanToken::KuduScanToken()
+    : data_(nullptr) {
 }
 
 KuduScanToken::~KuduScanToken() {
@@ -1327,8 +1332,8 @@ Status KuduScanToken::IntoKuduScanner(KuduScanner** 
scanner) const {
   return data_->IntoKuduScanner(scanner);
 }
 
-const vector<KuduTabletServer*>& KuduScanToken::TabletServers() const {
-  return data_->TabletServers();
+const KuduTablet& KuduScanToken::tablet() const {
+  return data_->tablet();
 }
 
 Status KuduScanToken::Serialize(string* buf) const {
@@ -1336,9 +1341,10 @@ Status KuduScanToken::Serialize(string* buf) const {
 }
 
 Status KuduScanToken::DeserializeIntoScanner(KuduClient* client,
-                                         const string& serialized_token,
-                                         KuduScanner** scanner) {
-  return KuduScanToken::Data::DeserializeIntoScanner(client, serialized_token, 
scanner);
+                                             const string& serialized_token,
+                                             KuduScanner** scanner) {
+  return KuduScanToken::Data::DeserializeIntoScanner(
+      client, serialized_token, scanner);
 }
 
 ////////////////////////////////////////////////////////////
@@ -1416,6 +1422,46 @@ Status 
KuduScanTokenBuilder::Build(vector<KuduScanToken*>* tokens) {
 }
 
 ////////////////////////////////////////////////////////////
+// KuduReplica
+////////////////////////////////////////////////////////////
+
+KuduReplica::KuduReplica()
+  : data_(nullptr) {
+}
+
+KuduReplica::~KuduReplica() {
+  delete data_;
+}
+
+bool KuduReplica::is_leader() const {
+  return data_->is_leader_;
+}
+
+const KuduTabletServer& KuduReplica::ts() const {
+  return *data_->ts_;
+}
+
+////////////////////////////////////////////////////////////
+// KuduTablet
+////////////////////////////////////////////////////////////
+
+KuduTablet::KuduTablet()
+  : data_(nullptr) {
+}
+
+KuduTablet::~KuduTablet() {
+  delete data_;
+}
+
+const string& KuduTablet::id() const {
+  return data_->id_;
+}
+
+const vector<const KuduReplica*>& KuduTablet::replicas() const {
+  return data_->replicas_;
+}
+
+////////////////////////////////////////////////////////////
 // KuduTabletServer
 ////////////////////////////////////////////////////////////
 
@@ -1432,7 +1478,11 @@ const string& KuduTabletServer::uuid() const {
 }
 
 const string& KuduTabletServer::hostname() const {
-  return data_->hostname_;
+  return data_->hp_.host();
+}
+
+uint16_t KuduTabletServer::port() const {
+  return data_->hp_.port();
 }
 
 } // namespace client

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/client.h
----------------------------------------------------------------------
diff --git a/src/kudu/client/client.h b/src/kudu/client/client.h
index f551765..2b33e9b 100644
--- a/src/kudu/client/client.h
+++ b/src/kudu/client/client.h
@@ -429,6 +429,94 @@ class KUDU_EXPORT KuduClient : public 
sp::enable_shared_from_this<KuduClient> {
   DISALLOW_COPY_AND_ASSIGN(KuduClient);
 };
 
+/// @brief In-memory representation of a remote tablet server.
+class KUDU_EXPORT KuduTabletServer {
+ public:
+  ~KuduTabletServer();
+
+  /// @return The UUID which is globally unique and guaranteed not to change
+  ///   for the lifetime of the tablet server.
+  const std::string& uuid() const;
+
+  /// @return Hostname of the first RPC address that this tablet server
+  ///   is listening on.
+  const std::string& hostname() const;
+
+  /// @return Port number of the first RPC address that this tablet server
+  ///   is listening on.
+  uint16_t port() const;
+
+ private:
+  class KUDU_NO_EXPORT Data;
+
+  friend class KuduClient;
+  friend class KuduScanner;
+  friend class KuduScanTokenBuilder;
+
+  KuduTabletServer();
+
+  // Owned.
+  Data* data_;
+
+  DISALLOW_COPY_AND_ASSIGN(KuduTabletServer);
+};
+
+/// @brief In-memory representation of a remote tablet's replica.
+class KUDU_EXPORT KuduReplica {
+ public:
+  ~KuduReplica();
+
+  /// @return Whether or not this replica is a Raft leader.
+  ///
+  /// @note This information may be stale; the role of a replica may change at
+  /// any time.
+  bool is_leader() const;
+
+  /// @return The tablet server hosting this remote replica.
+  const KuduTabletServer& ts() const;
+
+ private:
+  friend class KuduScanTokenBuilder;
+
+  class KUDU_NO_EXPORT Data;
+
+  KuduReplica();
+
+  // Owned.
+  Data* data_;
+
+  DISALLOW_COPY_AND_ASSIGN(KuduReplica);
+};
+
+/// @brief In-memory representation of a remote tablet.
+class KUDU_EXPORT KuduTablet {
+ public:
+  ~KuduTablet();
+
+  /// @return The ID which is globally unique and guaranteed not to change
+  ///    for the lifetime of the tablet.
+  const std::string& id() const;
+
+  /// @return The replicas of this tablet. The KuduTablet retains ownership
+  /// over the replicas.
+  ///
+  /// @note This information may be stale; replicas may be added or removed
+  /// from Raft configurations at any time.
+  const std::vector<const KuduReplica*>& replicas() const;
+
+ private:
+  friend class KuduScanTokenBuilder;
+
+  class KUDU_NO_EXPORT Data;
+
+  KuduTablet();
+
+  // Owned.
+  Data* data_;
+
+  DISALLOW_COPY_AND_ASSIGN(KuduTablet);
+};
+
 /// @brief A helper class to create a new table with the desired options.
 class KUDU_EXPORT KuduTableCreator {
  public:
@@ -1645,7 +1733,7 @@ class KUDU_EXPORT KuduScanner {
 /// instantiating the scanners on those nodes.
 ///
 /// Scan token locality information can be inspected using the
-/// KuduScanToken::TabletServers() method.
+/// KuduScanToken::tablet() function.
 class KUDU_EXPORT KuduScanToken {
  public:
 
@@ -1663,15 +1751,8 @@ class KUDU_EXPORT KuduScanToken {
   /// @return Operation result status.
   Status IntoKuduScanner(KuduScanner** scanner) const WARN_UNUSED_RESULT;
 
-  /// Get hint on candidate servers which may be hosting the source tablet.
-  ///
-  /// This method should be considered a hint, not a definitive answer,
-  /// since tablet to tablet server assignments may change in response to
-  /// external events such as failover or load balancing.
-  ///
-  /// @return Tablet servers who may be hosting the tablet which
-  ///   this scan is retrieving rows from.
-  const std::vector<KuduTabletServer*>& TabletServers() const;
+  /// @return Tablet that this scan will retrieve rows from.
+  const KuduTablet& tablet() const;
 
   /// Serialize the token into a string.
   ///
@@ -1702,7 +1783,7 @@ class KUDU_EXPORT KuduScanToken {
 
   friend class KuduScanTokenBuilder;
 
-  explicit KuduScanToken(Data* data);
+  KuduScanToken();
 
   // Owned.
   Data* data_;
@@ -1815,34 +1896,6 @@ class KUDU_EXPORT KuduScanTokenBuilder {
   DISALLOW_COPY_AND_ASSIGN(KuduScanTokenBuilder);
 };
 
-/// @brief In-memory representation of a remote tablet server.
-class KUDU_EXPORT KuduTabletServer {
- public:
-  ~KuduTabletServer();
-
-  /// @return The UUID which is globally unique and guaranteed not to change
-  ///   for the lifetime of the tablet server.
-  const std::string& uuid() const;
-
-  /// @return Hostname of the first RPC address that this tablet server
-  ///   is listening on.
-  const std::string& hostname() const;
-
- private:
-  class KUDU_NO_EXPORT Data;
-
-  friend class KuduClient;
-  friend class KuduScanner;
-  friend class KuduScanTokenBuilder;
-
-  KuduTabletServer();
-
-  // Owned.
-  Data* data_;
-
-  DISALLOW_COPY_AND_ASSIGN(KuduTabletServer);
-};
-
 } // namespace client
 } // namespace kudu
 #endif

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/meta_cache.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/meta_cache.cc b/src/kudu/client/meta_cache.cc
index 9ae6714..d3507ef 100644
--- a/src/kudu/client/meta_cache.cc
+++ b/src/kudu/client/meta_cache.cc
@@ -135,7 +135,7 @@ void RemoteTabletServer::Update(const master::TSInfoPB& pb) 
{
   }
 }
 
-string RemoteTabletServer::permanent_uuid() const {
+const string& RemoteTabletServer::permanent_uuid() const {
   return uuid_;
 }
 
@@ -231,6 +231,7 @@ bool RemoteTablet::HasLeader() const {
 }
 
 void RemoteTablet::GetRemoteTabletServers(vector<RemoteTabletServer*>* 
servers) const {
+  servers->clear();
   std::lock_guard<simple_spinlock> l(lock_);
   for (const RemoteReplica& replica : replicas_) {
     if (replica.failed) {
@@ -240,6 +241,17 @@ void 
RemoteTablet::GetRemoteTabletServers(vector<RemoteTabletServer*>* servers)
   }
 }
 
+void RemoteTablet::GetRemoteReplicas(vector<RemoteReplica>* replicas) const {
+  replicas->clear();
+  std::lock_guard<simple_spinlock> l(lock_);
+  for (const auto& r : replicas_) {
+    if (r.failed) {
+      continue;
+    }
+    replicas->push_back(r);
+  }
+}
+
 void RemoteTablet::MarkTServerAsLeader(const RemoteTabletServer* server) {
   bool found = false;
   std::lock_guard<simple_spinlock> l(lock_);

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/meta_cache.h
----------------------------------------------------------------------
diff --git a/src/kudu/client/meta_cache.h b/src/kudu/client/meta_cache.h
index b621500..a46a8a8 100644
--- a/src/kudu/client/meta_cache.h
+++ b/src/kudu/client/meta_cache.h
@@ -92,7 +92,7 @@ class RemoteTabletServer {
   void GetHostPorts(std::vector<HostPort>* host_ports) const;
 
   // Returns the remote server's uuid.
-  std::string permanent_uuid() const;
+  const std::string& permanent_uuid() const;
 
  private:
   // Internal callback for DNS resolution.
@@ -214,6 +214,9 @@ class RemoteTablet : public 
RefCountedThreadSafe<RemoteTablet> {
   // failed replicas.
   void GetRemoteTabletServers(std::vector<RemoteTabletServer*>* servers) const;
 
+  // Writes this tablet's replicas to 'replicas'. Skips failed replicas.
+  void GetRemoteReplicas(std::vector<RemoteReplica>* replicas) const;
+
   // Return true if the tablet currently has a known LEADER replica
   // (i.e the next call to LeaderTServer() is likely to return non-NULL)
   bool HasLeader() const;

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/replica-internal.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/replica-internal.cc 
b/src/kudu/client/replica-internal.cc
new file mode 100644
index 0000000..0fac695
--- /dev/null
+++ b/src/kudu/client/replica-internal.cc
@@ -0,0 +1,38 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "kudu/client/replica-internal.h"
+
+#include <memory>
+
+#include "kudu/client/client.h"
+
+namespace kudu {
+namespace client {
+
+using std::unique_ptr;
+
+KuduReplica::Data::Data(bool is_leader, unique_ptr<KuduTabletServer> ts)
+    : is_leader_(is_leader),
+      ts_(std::move(ts)) {
+}
+
+KuduReplica::Data::~Data() {
+}
+
+} // namespace client
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/replica-internal.h
----------------------------------------------------------------------
diff --git a/src/kudu/client/replica-internal.h 
b/src/kudu/client/replica-internal.h
new file mode 100644
index 0000000..9f0674d
--- /dev/null
+++ b/src/kudu/client/replica-internal.h
@@ -0,0 +1,42 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+#pragma once
+
+#include <memory>
+
+#include "kudu/client/client.h"
+#include "kudu/gutil/macros.h"
+
+
+namespace kudu {
+namespace client {
+
+class KuduTabletServer;
+
+class KuduReplica::Data {
+ public:
+  Data(bool is_leader, std::unique_ptr<KuduTabletServer> ts);
+  ~Data();
+
+  const bool is_leader_;
+  const std::unique_ptr<KuduTabletServer> ts_;
+
+  DISALLOW_COPY_AND_ASSIGN(Data);
+};
+
+} // namespace client
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/scan_token-internal.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/scan_token-internal.cc 
b/src/kudu/client/scan_token-internal.cc
index c724daf..a9bddd5 100644
--- a/src/kudu/client/scan_token-internal.cc
+++ b/src/kudu/client/scan_token-internal.cc
@@ -25,30 +25,33 @@
 #include "kudu/client/client-internal.h"
 #include "kudu/client/client.h"
 #include "kudu/client/meta_cache.h"
+#include "kudu/client/replica-internal.h"
 #include "kudu/client/scanner-internal.h"
+#include "kudu/client/tablet-internal.h"
 #include "kudu/client/tablet_server-internal.h"
 #include "kudu/common/wire_protocol.h"
 #include "kudu/gutil/stl_util.h"
+#include "kudu/gutil/strings/substitute.h"
 #include "kudu/util/pb_util.h"
 #include "kudu/util/status.h"
 
 using std::string;
 using std::unique_ptr;
 using std::vector;
+using strings::Substitute;
 
 namespace kudu {
 namespace client {
 
 KuduScanToken::Data::Data(KuduTable* table,
                           ScanTokenPB message,
-                          vector<KuduTabletServer*> tablet_servers)
+                          unique_ptr<KuduTablet> tablet)
     : table_(table),
       message_(std::move(message)),
-      tablet_servers_(std::move(tablet_servers)) {
+      tablet_(std::move(tablet)) {
 }
 
 KuduScanToken::Data::~Data() {
-  ElementDeleter deleter(&tablet_servers_);
 }
 
 Status KuduScanToken::Data::IntoKuduScanner(KuduScanner** scanner) const {
@@ -96,7 +99,7 @@ Status KuduScanToken::Data::PBIntoScanner(KuduClient* client,
     }
     DataType expectedType = schema->column(columnIdx).type_info()->type();
     if (column.type() != expectedType) {
-      return Status::InvalidArgument(strings::Substitute(
+      return Status::InvalidArgument(Substitute(
             "invalid type $0 for column '$1' in scan token, expected: $2",
             column.type(), column.name(), expectedType));
     }
@@ -245,31 +248,49 @@ Status 
KuduScanTokenBuilder::Data::Build(vector<KuduScanToken*>* tokens) {
       continue;
     }
 
-    vector<internal::RemoteTabletServer*> remote_tablet_servers;
-    tablet->GetRemoteTabletServers(&remote_tablet_servers);
+    vector<internal::RemoteReplica> replicas;
+    tablet->GetRemoteReplicas(&replicas);
 
-    vector<KuduTabletServer*> tablet_servers;
-    ElementDeleter deleter(&tablet_servers);
+    vector<const KuduReplica*> client_replicas;
+    ElementDeleter deleter(&client_replicas);
 
-    for (internal::RemoteTabletServer* remote_tablet_server : 
remote_tablet_servers) {
+    // Convert the replicas from their internal format to something appropriate
+    // for clients.
+    for (const auto& r : replicas) {
       vector<HostPort> host_ports;
-      remote_tablet_server->GetHostPorts(&host_ports);
+      r.ts->GetHostPorts(&host_ports);
       if (host_ports.empty()) {
-        return Status::IllegalState(strings::Substitute("No host found for 
tablet server $0",
-                                                        
remote_tablet_server->ToString()));
+        return Status::IllegalState(Substitute(
+            "No host found for tablet server $0", r.ts->ToString()));
       }
-      KuduTabletServer* tablet_server = new KuduTabletServer;
-      tablet_server->data_ = new 
KuduTabletServer::Data(remote_tablet_server->permanent_uuid(),
-                                                        host_ports[0].host());
-      tablet_servers.push_back(tablet_server);
+      unique_ptr<KuduTabletServer> client_ts(new KuduTabletServer);
+      client_ts->data_ = new KuduTabletServer::Data(r.ts->permanent_uuid(),
+                                                    host_ports[0]);
+      bool is_leader = r.role == consensus::RaftPeerPB::LEADER;
+      unique_ptr<KuduReplica> client_replica(new KuduReplica);
+      client_replica->data_ = new KuduReplica::Data(is_leader,
+                                                    std::move(client_ts));
+      client_replicas.push_back(client_replica.release());
     }
+
+    unique_ptr<KuduTablet> client_tablet(new KuduTablet);
+    client_tablet->data_ = new KuduTablet::Data(tablet->tablet_id(),
+                                                std::move(client_replicas));
+    client_replicas.clear();
+
+    // Create the scan token itself.
     ScanTokenPB message;
     message.CopyFrom(pb);
-    
message.set_lower_bound_partition_key(tablet->partition().partition_key_start());
-    
message.set_upper_bound_partition_key(tablet->partition().partition_key_end());
-    tokens->push_back(new KuduScanToken(new KuduScanToken::Data(table,
-                                                                
std::move(message),
-                                                                
std::move(tablet_servers))));
+    message.set_lower_bound_partition_key(
+        tablet->partition().partition_key_start());
+    message.set_upper_bound_partition_key(
+        tablet->partition().partition_key_end());
+    unique_ptr<KuduScanToken> client_scan_token(new KuduScanToken);
+    client_scan_token->data_ =
+        new KuduScanToken::Data(table,
+                                std::move(message),
+                                std::move(client_tablet));
+    tokens->push_back(client_scan_token.release());
     pruner.RemovePartitionKeyRange(tablet->partition().partition_key_end());
   }
   return Status::OK();

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/scan_token-internal.h
----------------------------------------------------------------------
diff --git a/src/kudu/client/scan_token-internal.h 
b/src/kudu/client/scan_token-internal.h
index c2096a1..65bef42 100644
--- a/src/kudu/client/scan_token-internal.h
+++ b/src/kudu/client/scan_token-internal.h
@@ -17,8 +17,9 @@
 
 #pragma once
 
-#include <vector>
+#include <memory>
 #include <string>
+#include <vector>
 
 #include "kudu/client/client.h"
 #include "kudu/client/client.pb.h"
@@ -31,13 +32,13 @@ class KuduScanToken::Data {
  public:
   explicit Data(KuduTable* table,
                 ScanTokenPB message,
-                std::vector<KuduTabletServer*> tablet_servers);
+                std::unique_ptr<KuduTablet> tablet);
   ~Data();
 
   Status IntoKuduScanner(KuduScanner** scanner) const;
 
-  const std::vector<KuduTabletServer*>& TabletServers() const {
-    return tablet_servers_;
+  const KuduTablet& tablet() const {
+    return *tablet_;
   }
 
   Status Serialize(std::string* buf) const;
@@ -47,14 +48,13 @@ class KuduScanToken::Data {
                                        KuduScanner** scanner);
 
  private:
-
   static Status PBIntoScanner(KuduClient* client,
                               const ScanTokenPB& message,
                               KuduScanner** scanner);
 
-  KuduTable* table_;
-  ScanTokenPB message_;
-  std::vector<KuduTabletServer*> tablet_servers_;
+  const KuduTable* table_;
+  const ScanTokenPB message_;
+  const std::unique_ptr<KuduTablet> tablet_;
 };
 
 class KuduScanTokenBuilder::Data {

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/scan_token-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/scan_token-test.cc 
b/src/kudu/client/scan_token-test.cc
index 05bb93d..95de9f1 100644
--- a/src/kudu/client/scan_token-test.cc
+++ b/src/kudu/client/scan_token-test.cc
@@ -19,23 +19,27 @@
 #include <memory>
 #include <string>
 #include <thread>
+#include <unordered_set>
 #include <vector>
 
 #include "kudu/client/client.h"
 #include "kudu/gutil/stl_util.h"
 #include "kudu/integration-tests/mini_cluster.h"
+#include "kudu/tserver/mini_tablet_server.h"
+#include "kudu/tserver/tablet_server.h"
 #include "kudu/util/test_util.h"
 
+namespace kudu {
+namespace client {
+
+using sp::shared_ptr;
 using std::atomic;
 using std::string;
 using std::thread;
 using std::unique_ptr;
+using std::unordered_set;
 using std::vector;
-
-namespace kudu {
-namespace client {
-
-using sp::shared_ptr;
+using tserver::MiniTabletServer;
 
 class ScanTokenTest : public KuduTest {
 
@@ -101,6 +105,31 @@ class ScanTokenTest : public KuduTest {
     return rows;
   }
 
+  void VerifyTabletInfo(const vector<KuduScanToken*>& tokens) {
+    unordered_set<string> tablet_ids;
+    for (auto t : tokens) {
+      tablet_ids.insert(t->tablet().id());
+
+      // Check that there's only one replica; this is a non-replicated table.
+      ASSERT_EQ(1, t->tablet().replicas().size());
+
+      // Check that this replica is a leader; since there's only one tserver,
+      // it must be.
+      const KuduReplica* r = t->tablet().replicas()[0];
+      ASSERT_TRUE(r->is_leader());
+
+      // Check that the tserver associated with the replica is the sole tserver
+      // started for this cluster.
+      const MiniTabletServer* ts = cluster_->mini_tablet_server(0);
+      ASSERT_EQ(ts->server()->instance_pb().permanent_uuid(),
+                r->ts().uuid());
+      ASSERT_EQ(ts->bound_rpc_addr().host(), r->ts().hostname());
+      ASSERT_EQ(ts->bound_rpc_addr().port(), r->ts().port());
+    }
+    // Check that there are no duplicate tablet IDs.
+    ASSERT_EQ(tokens.size(), tablet_ids.size());
+  }
+
   shared_ptr<KuduClient> client_;
   gscoped_ptr<MiniCluster> cluster_;
 };
@@ -149,6 +178,7 @@ TEST_F(ScanTokenTest, TestScanTokens) {
 
     ASSERT_EQ(8, tokens.size());
     ASSERT_EQ(200, CountRows(tokens));
+    NO_FATALS(VerifyTabletInfo(tokens));
   }
 
   { // range predicate
@@ -163,6 +193,7 @@ TEST_F(ScanTokenTest, TestScanTokens) {
 
     ASSERT_EQ(4, tokens.size());
     ASSERT_EQ(100, CountRows(tokens));
+    NO_FATALS(VerifyTabletInfo(tokens));
   }
 
   { // equality predicate
@@ -177,6 +208,7 @@ TEST_F(ScanTokenTest, TestScanTokens) {
 
     ASSERT_EQ(1, tokens.size());
     ASSERT_EQ(1, CountRows(std::move(tokens)));
+    NO_FATALS(VerifyTabletInfo(tokens));
   }
 
   { // primary key bound
@@ -191,6 +223,7 @@ TEST_F(ScanTokenTest, TestScanTokens) {
 
     ASSERT_EQ(4, tokens.size());
     ASSERT_EQ(60, CountRows(tokens));
+    NO_FATALS(VerifyTabletInfo(tokens));
   }
 }
 
@@ -257,6 +290,7 @@ TEST_F(ScanTokenTest, TestScanTokensWithNonCoveringRange) {
 
     ASSERT_EQ(6, tokens.size());
     ASSERT_EQ(300, CountRows(tokens));
+    NO_FATALS(VerifyTabletInfo(tokens));
   }
 
   { // range predicate
@@ -271,6 +305,7 @@ TEST_F(ScanTokenTest, TestScanTokensWithNonCoveringRange) {
 
     ASSERT_EQ(4, tokens.size());
     ASSERT_EQ(200, CountRows(tokens));
+    NO_FATALS(VerifyTabletInfo(tokens));
   }
 
   { // equality predicate
@@ -284,7 +319,8 @@ TEST_F(ScanTokenTest, TestScanTokensWithNonCoveringRange) {
     ASSERT_OK(builder.Build(&tokens));
 
     ASSERT_EQ(1, tokens.size());
-    ASSERT_EQ(1, CountRows(std::move(tokens)));
+    ASSERT_EQ(1, CountRows(tokens));
+    NO_FATALS(VerifyTabletInfo(tokens));
   }
 
   { // primary key bound
@@ -299,6 +335,7 @@ TEST_F(ScanTokenTest, TestScanTokensWithNonCoveringRange) {
 
     ASSERT_EQ(2, tokens.size());
     ASSERT_EQ(40, CountRows(tokens));
+    NO_FATALS(VerifyTabletInfo(tokens));
   }
 }
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/tablet-internal.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/tablet-internal.cc 
b/src/kudu/client/tablet-internal.cc
new file mode 100644
index 0000000..06b49a0
--- /dev/null
+++ b/src/kudu/client/tablet-internal.cc
@@ -0,0 +1,42 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "kudu/client/tablet-internal.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "kudu/gutil/stl_util.h"
+
+using std::string;
+using std::vector;
+
+namespace kudu {
+namespace client {
+
+KuduTablet::Data::Data(string id, vector<const KuduReplica*> replicas)
+    : id_(std::move(id)),
+      replicas_(std::move(replicas)) {
+}
+
+KuduTablet::Data::~Data() {
+  STLDeleteElements(&replicas_);
+}
+
+} // namespace client
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/tablet-internal.h
----------------------------------------------------------------------
diff --git a/src/kudu/client/tablet-internal.h 
b/src/kudu/client/tablet-internal.h
new file mode 100644
index 0000000..401c725
--- /dev/null
+++ b/src/kudu/client/tablet-internal.h
@@ -0,0 +1,42 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "kudu/client/client.h"
+#include "kudu/gutil/macros.h"
+
+namespace kudu {
+namespace client {
+
+class KuduReplica;
+
+class KuduTablet::Data {
+ public:
+  Data(std::string id, std::vector<const KuduReplica*> replicas);
+  ~Data();
+
+  const std::string id_;
+  std::vector<const KuduReplica*> replicas_;
+
+  DISALLOW_COPY_AND_ASSIGN(Data);
+};
+
+} // namespace client
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/tablet_server-internal.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/tablet_server-internal.cc 
b/src/kudu/client/tablet_server-internal.cc
index d5ad1c7..057fd2d 100644
--- a/src/kudu/client/tablet_server-internal.cc
+++ b/src/kudu/client/tablet_server-internal.cc
@@ -17,14 +17,18 @@
 
 #include "kudu/client/tablet_server-internal.h"
 
+#include <string>
+
+#include "kudu/util/net/net_util.h"
+
 using std::string;
 
 namespace kudu {
 namespace client {
 
-KuduTabletServer::Data::Data(string uuid, string hostname)
+KuduTabletServer::Data::Data(string uuid, HostPort hp)
     : uuid_(std::move(uuid)),
-      hostname_(std::move(hostname)) {
+      hp_(std::move(hp)) {
 }
 
 KuduTabletServer::Data::~Data() {

http://git-wip-us.apache.org/repos/asf/kudu/blob/6a92db4d/src/kudu/client/tablet_server-internal.h
----------------------------------------------------------------------
diff --git a/src/kudu/client/tablet_server-internal.h 
b/src/kudu/client/tablet_server-internal.h
index edc903f..0e57ac4 100644
--- a/src/kudu/client/tablet_server-internal.h
+++ b/src/kudu/client/tablet_server-internal.h
@@ -21,17 +21,18 @@
 
 #include "kudu/client/client.h"
 #include "kudu/gutil/macros.h"
+#include "kudu/util/net/net_util.h"
 
 namespace kudu {
 namespace client {
 
 class KuduTabletServer::Data {
  public:
-  Data(std::string uuid, std::string hostname);
+  Data(std::string uuid, HostPort hp);
   ~Data();
 
   const std::string uuid_;
-  const std::string hostname_;
+  const HostPort hp_;
 
   DISALLOW_COPY_AND_ASSIGN(Data);
 };

Reply via email to