<p>NameNodes have scalability limits because of the metadata overhead comprised of inodes (files and directories) and file blocks, the number of Datanode heartbeats, and the number of HDFS RPC client requests. The common solution is to split the filesystem into smaller subclusters <ahref="../hadoop-hdfs/Federation.html">HDFS Federation</a> and provide a federated view <ahref="../hadoop-hdfs/ViewFs.html">ViewFs</a>. The problem is how to maintain the split of the subclusters (e.g., namespace partition), which forces users to connect to multiple subclusters and manage the allocation of folders/files to them.</p></section><section>
<h2><aname="Architecture"></a>Architecture</h2>
<p>A natural extension to this partitioned federation is to add a layer of software responsible for federating the namespaces. This extra layer allows users to access any subcluster transparently, lets subclusters manage their own block pools independently, and will support rebalancing of data across subclusters later (see more info in <aclass="externalLink"href="https://issues.apache.org/jira/browse/HDFS-13123">HDFS-13123</a>). The subclusters in RBF are not required to be the independent HDFS clusters, a normal federation cluster (with multiple block pools) or a mixed cluster with federation and independent cluster is also allowed. To accomplish these goals, the federation layer directs block accesses to the proper subcluster, maintains the state of the namespaces, and provides mechanisms for data rebalancing. This layer must be scalable, highly available, and fault tolerant.</p>
<p>This federation layer comprises multiple components. The <i>Router</i> component that has the same interface as a NameNode, and forwards the client requests to the correct subcluster, based on ground-truth information from a State Store. The <i>State Store</i> combines a remote <i>Mount Table</i> (in the flavor of <ahref="../hadoop-hdfs/ViewFs.html">ViewFs</a>, but shared between clients) and utilization (load/capacity) information about the subclusters. This approach has the same architecture as <ahref="../../hadoop-yarn/hadoop-yarn-site/Federation.html">YARN federation</a>.</p>
<p>The simplest configuration deploys a Router on each NameNode machine. The Router monitors the local NameNode and its state and heartbeats to the State Store. When a regular DFS client contacts any of the Routers to access a file in the federated filesystem, the Router checks the Mount Table in the State Store (i.e., the local cache) to find out which subcluster contains the file. Then it checks the Membership table in the State Store (i.e., the local cache) for the NameNode responsible for the subcluster. After it has identified the correct NameNode, the Router proxies the request. The client accesses Datanodes directly.</p></section><section>
<h3><aname="Router"></a>Router</h3>
<p>There can be multiple Routers in the system with soft state. Each Router has two roles:</p>
<ul>
<li>Federated interface: expose a single, global NameNode interface to the clients and forward the requests to the active NameNode in the correct subcluster</li>
<li>NameNode heartbeat: maintain the information about a NameNode in the State Store</li>
<p>The Router receives a client request, checks the State Store for the correct subcluster, and forwards the request to the active NameNode of that subcluster. The reply from the NameNode then flows in the opposite direction. The Routers are stateless and can be behind a load balancer. For health checking, you can use /isActive endpoint as a health probe (e.g. <aclass="externalLink"href="http://ROUTER_HOSTNAME:ROUTER_PORT/isActive">http://ROUTER_HOSTNAME:ROUTER_PORT/isActive</a>). For performance, the Router also caches remote mount table entries and the state of the subclusters. To make sure that changes have been propagated to all Routers, each Router heartbeats its state to the State Store.</p>
<p>The communications between the Routers and the State Store are cached (with timed expiration for freshness). This improves the performance of the system.</p></section><section>
<p>For this role, the Router periodically checks the state of a NameNode (usually on the same server) and reports their high availability (HA) state and load/space status to the State Store. Note that this is an optional role, as a Router can be independent of any subcluster. For performance with NameNode HA, the Router uses the high availability state information in the State Store to forward the request to the NameNode that is most likely to be active. Note that this service can be embedded into the NameNode itself to simplify the operation.</p></section><section>
<h4><aname="Availability_and_fault_tolerance"></a>Availability and fault tolerance</h4>
<p>The Router operates with failures at multiple levels.</p>
<ul>
<li>
<p><b>Federated interface HA:</b> The Routers are stateless and metadata operations are atomic at the NameNodes. If a Router becomes unavailable, any Router can take over for it. The clients configure their DFS HA client (e.g., ConfiguredFailoverProvider or RequestHedgingProxyProvider) with all the Routers in the federation as endpoints.</p>
</li>
<li>
<p><b>Unavailable State Store:</b> If a Router cannot contact the State Store, it will enter into a Safe Mode state which disallows it from serving requests. Clients will treat Routers in Safe Mode as it was an Standby NameNode and try another Router. There is a manual way to manage the Safe Mode for the Router.</p>
</li>
</ul>
<p>The Safe Mode state can be managed by using the following command:</p>
<divclass="source">
<divclass="source">
<pre>[hdfs]$ $HADOOP_HOME/bin/hdfs dfsrouteradmin -safemode enter | leave | get
</pre></div></div>
<ul>
<li>
<p><b>NameNode heartbeat HA:</b> For high availability and flexibility, multiple Routers can monitor the same NameNode and heartbeat the information to the State Store. This increases clients’ resiliency to stale information, should a Router fail. Conflicting NameNode information in the State Store is resolved by each Router via a quorum.</p>
</li>
<li>
<p><b>Unavailable NameNodes:</b> If a Router cannot contact the active NameNode, then it will try the other NameNodes in the subcluster. It will first try those reported as standby and then the unavailable ones. If the Router cannot reach any NameNode, then it throws an exception.</p>
</li>
<li>
<p><b>Expired NameNodes:</b> If a NameNode heartbeat has not been recorded in the State Store for a multiple of the heartbeat interval, the monitoring Router will record that the NameNode has expired and no Routers will attempt to access it. If an updated heartbeat is subsequently recorded for the NameNode, the monitoring Router will restore the NameNode from the expired state.</p>
</li>
</ul></section><section>
<h4><aname="Interfaces"></a>Interfaces</h4>
<p>To interact with the users and the administrators, the Router exposes multiple interfaces.</p>
<ul>
<li>
<p><b>RPC:</b> The Router RPC implements the most common interfaces clients use to interact with HDFS. The current implementation has been tested using analytics workloads written in plain MapReduce, Spark, and Hive (on Tez, Spark, and MapReduce). Advanced functions like snapshot, encryption and tiered storage are left for future versions. All unimplemented functions will throw exceptions.</p>
</li>
<li>
<p><b>Admin:</b> Administrators can query information from clusters and add/remove entries from the mount table over RPC. This interface is also exposed through the command line to get and modify information from the federation.</p>
</li>
<li>
<p><b>Web UI:</b> The Router exposes a Web UI visualizing the state of the federation, mimicking the current NameNode UI. It displays information about the mount table, membership information about each subcluster, and the status of the Routers.</p>
</li>
<li>
<p><b>WebHDFS:</b> The Router provides the HDFS REST interface (WebHDFS) in addition to the RPC one.</p>
</li>
<li>
<p><b>JMX:</b> It exposes metrics through JMX mimicking the NameNode. This is used by the Web UI to get the cluster status.</p>
</li>
</ul>
<p>Some operations are not available in Router-based federation. The Router throws exceptions for those. Examples users may encounter include the following.</p>
<ul>
<li>Rename file/folder in two different nameservices.</li>
<li>Copy file/folder in two different nameservices.</li>
<li>Write into a file/folder being rebalanced.</li>
<p>Federation supports and controls global quota at mount table level. For performance reasons, the Router caches the quota usage and updates it periodically. These quota usage values will be used for quota-verification during each WRITE RPC call invoked in RouterRPCSever. See <ahref="../hadoop-hdfs/HdfsQuotaAdminGuide.html">HDFS Quotas Guide</a> for the quota detail.</p>
<p>Note: When global quota is enabled, setting or clearing sub-cluster’s quota directly is not recommended since Router Admin server will override sub-cluster’s quota with global quota.</p></section><section>
<h3><aname="State_Store"></a>State Store</h3>
<p>The (logically centralized, but physically distributed) State Store maintains:</p>
<ul>
<li>The state of the subclusters in terms of their block access load, available disk space, HA state, etc.</li>
<li>The mapping between folder/files and subclusters, i.e. the remote mount table.</li>
</ul>
<p>The backend of the State Store is pluggable. We leverage the fault tolerance of the backend implementations. The main information stored in the State Store and its implementation:</p>
<ul>
<li>
<p><b>Membership</b>: The membership information encodes the state of the NameNodes in the federation. This includes information about the subcluster, such as storage capacity and the number of nodes. The Router periodically heartbeats this information about one or more NameNodes. Given that multiple Routers can monitor a single NameNode, the heartbeat from every Router is stored. The Routers apply a quorum of the data when querying this information from the State Store. The Routers discard the entries older than a certain threshold (e.g., ten Router heartbeat periods).</p>
</li>
<li>
<p><b>Mount Table</b>: This table hosts the mapping between folders and subclusters. It is similar to the mount table in <ahref="../hadoop-hdfs/ViewFs.html">ViewFs</a> where it specifies the federated folder, the destination subcluster and the path in that folder.</p>
</li>
</ul></section><section>
<h3><aname="Security"></a>Security</h3>
<p>Router supports security similar to <ahref="../hadoop-common/SecureMode.html">current security model</a> in HDFS. This feature is available for both RPC and Web based calls. It has the capability to proxy to underlying secure HDFS clusters.</p>
<p>Similar to Namenode, support exists for both kerberos and token based authentication for clients connecting to routers. Router internally relies on existing security related configs of <code>core-site.xml</code> and <code>hdfs-site.xml</code> to support this feature. In addition to that, routers need to be configured with its own keytab and principal.</p>
<p>For token based authentication, router issues delegation tokens to upstream clients without communicating with downstream namenodes. Router uses its own credentials to securely proxy to downstream namenode on behalf of upstream real user. Router principal has to be configured as a superuser in all secure downstream namenodes. Refer <ahref="../hadoop-common/Superusers.html">here</a> to configure proxy user for namenode. Along with that, user owning router daemons should be configured with the same identity as namenode process itself. Refer <ahref="../hadoop-hdfs/HdfsPermissionsGuide.html#The_Super-User">here</a> for details. Router relies on a state store to distribute tokens across all routers. Apart from default implementation provided users can plugin their own implementation of state store for token management. Default implementation relies on zookeeper for token management. Since a large router/zookeeper cluster could potentially hold millions of tokens, <code>jute.maxbuffer</code> system property that zookeeper clients rely on should be appropriately configured in router daemons.</p>
<p>See the Apache JIRA ticket <aclass="externalLink"href="https://issues.apache.org/jira/browse/HDFS-13532">HDFS-13532</a> for more information on this feature.</p></section><section>
<h3><aname="Isolation"></a>Isolation</h3>
<p>Router supports assignment of the dedicated number of RPC handlers to achieve isolation for all downstream nameservices it is configured to proxy. Since large or busy clusters may have relatively higher RPC traffic to the namenode compared to other clusters namenodes, this feature if enabled allows admins to configure higher number of RPC handlers for busy clusters. If dedicated handlers are not assigned for specific nameservices, equal distribution of RPC handlers is done for all configured nameservices. <b>Note</b> Fanout calls are treated as targeting a special nameservice, thus can be configured with handlers as well.</p>
<p>If a downstream namenode is slow/busy enough that permits are unavailable, routers would throw StandByException exception to the client. This would in turn trigger a failover behavior at the client side and clients would connect to a different router in the cluster. This offers a positive effect of automatically load balancing RPCs across all routers nodes. This is important to ensure that a single router does not become a bottleneck in case of unhealthy namenodes and all handlers available in the entire router cluster are utilized.</p>
<p>Users can configure handlers based on steady state load that individual downstream namenodes expect and can introduce more routers to the cluster to handle more RPCs overall. Because of the bouncing behavior that clients automatically get in this feature in an event where certain namenodes are overloaded, good clients connecting to good namenodes will always continue to use Rpc lanes dedicated to them. For bad behaving namenodes or backfill jobs that put spiky loads on namenodes, more routers could potentially be added with a higher than usual handler count to deal with the surge in traffic for specific nameservices if needed.</p>
<p>Overall the isolation feature is exposed via a configuration dfs.federation.router.handler.isolation.enable. The default value of this feature will be “false”. Users can also introduce their own fairness policy controller for custom allocation of handlers to various nameservices.</p>
<p>See the Apache JIRA ticket <aclass="externalLink"href="https://issues.apache.org/jira/browse/HDFS-14090">HDFS-14090</a> for more information on this feature.</p></section></section><section>
<h2><aname="Deployment"></a>Deployment</h2>
<p>By default, the Router is ready to take requests and monitor the NameNode in the local machine. It needs to know the State Store endpoint by setting <code>dfs.federation.router.store.driver.class</code>. The rest of the options are documented in <ahref="../hadoop-hdfs-rbf/hdfs-rbf-default.xml">hdfs-rbf-default.xml</a>.</p>
<p>Once the Router is configured, it can be started:</p>
<p>The mount table entries are pretty much the same as in <ahref="../hadoop-hdfs/ViewFs.html">ViewFs</a>. Please make sure the downstream namespace path exists before creating mount table entry pointing to it. A good practice for simplifying the management is to name the federated namespace with the same names as the destination namespaces. For example, if we to mount <code>/data/app1</code> in the federated namespace, it is recommended to have that same name as in the destination namespace.</p>
<p>The federation admin tool supports managing the mount table. For example, to create three mount points and list them:</p>
<p>If a mount point is not set, the Router will map it to the default namespace <code>dfs.federation.router.default.nameserviceId</code>.</p>
<p>Mount table have UNIX-like <i>permissions</i>, which restrict which users and groups have access to the mount point. Write permissions allow users to add , update or remove mount point. Read permissions allow users to list mount point. Execute permissions are unused.</p>
<p>Mount table permission can be set by following command:</p>
<p>The option mode is UNIX-style permissions for the mount table. Permissions are specified in octal, e.g. 0755. By default, this is set to 0755.</p><section>
<h4><aname="Quotas"></a>Quotas</h4>
<p>Router-based federation supports global quota at mount table level. Mount table entries may spread multiple subclusters and the global quota will be accounted across these subclusters.</p>
<p>The federation admin tool supports setting quotas for specified mount table entries:</p>
<p>The above command means that we allow the path to have a maximum of 100 file/directories and use at most 1024 bytes storage space. The parameter for <i>ssQuota</i> supports multiple size-unit suffix (e.g. 1k is 1KB, 5m is 5MB). If no suffix is specified then bytes is assumed.</p>
<p>Set storage type quota for specified mount table entry:</p>
<p>The above command will refresh cache of the connected router. This command is redundant when mount table refresh service is enabled as the service will always keep the cache updated.</p></section><section>
<p>A mount point also supports mapping multiple subclusters. For example, to create a mount point that stores files in subclusters <code>ns1</code> and <code>ns2</code>.</p>
<divclass="source">
<divclass="source">
<pre>[hdfs]$ $HADOOP_HOME/bin/hdfs dfsrouteradmin -add /data ns1,ns2 /data -order SPACE
</pre></div></div>
<p>When listing <code>/data</code>, it will show all the folders and files in both subclusters. For deciding where to create a new file/folder it uses the order parameter, it currently supports the following methods:</p>
<ul>
<li>HASH: Follow consistent hashing in the first level. Deeper levels will be in the one of the parent.</li>
<li>LOCAL: Try to write data in the local subcluster.</li>
<li>RANDOM: Random subcluster. This is usually useful for balancing the load across. Folders are created in all subclusters.</li>
<li>HASH_ALL: Follow consistent hashing at all the levels. This approach tries to balance the reads and writes evenly across subclusters. Folders are created in all subclusters.</li>
<li>SPACE: Try to write data in the subcluster with the most available space. Folders are created in all subclusters.</li>
</ul>
<p>For the hash-based approaches, the difference is that HASH would make all the files/folders within a folder belong to the same subcluster while HASH_ALL will spread all files under a mount point. For example, assuming we have a HASH mount point for <code>/data/hash</code>, files and folders under <code>/data/hash/folder0</code> will all be in the same subcluster. On the other hand, a HASH_ALL mount point for <code>/data/hash_all</code>, will spread files under <code>/data/hash_all/folder0</code> across all the subclusters for that mount point (subfolders will be created to all subclusters).</p>
<p>RANDOM can be used for reading and writing data from/into different subclusters. The common use for this approach is to have the same data in multiple subclusters and balance the reads across subclusters. For example, if thousands of containers need to read the same data (e.g., a library), one can use RANDOM to read the data from any of the subclusters.</p>
<p>To determine which subcluster contains a file:</p>
<p>Note that consistency of the data across subclusters is not guaranteed by the Router. By default, if one subcluster is unavailable, writes may fail if they target that subcluster. To allow writing in another subcluster, one can make the mount point fault tolerant:</p>
<p>Note that this can lead to a file to be written in multiple subclusters or a folder missing in one. One needs to be aware of the possibility of these inconsistencies and target this <code>faulttolerant</code> approach to resilient paths. An example for this is the <code>/app-logs</code> folder which will mostly write once into a subfolder.</p></section></section><section>
<p>To prevent accessing a nameservice (sublcuster), it can be disabled from the federation. For example, one can disable <code>ns1</code>, list it and enable it again:</p>
<p>This is useful when decommissioning subclusters or when one subcluster is missbehaving (e.g., low performance or unavailability).</p></section><section>
<h3><aname="Router_server_generically_refresh"></a>Router server generically refresh</h3>
<p>To trigger a runtime-refresh of the resource specified by <key> on <host:ipc_port>. For example, to enable white list checking, we just need to send a refresh command other than restart the router server.</p>
<h3><aname="Router_state_dump"></a>Router state dump</h3>
<p>To diagnose the current state of the routers, you can use the dumpState command. It generates a text dump of the records in the State Store. Since it uses the configuration to find and read the state store, it is often easiest to use the machine where the routers run. The command runs locally, so the routers do not have to be up to use this command.</p>
<p>For clients to use the federated namespace, they need to create a new one that points to the routers. For example, a cluster with 4 namespaces <b>ns0, ns1, ns2, ns3</b>, can add a new one to <b>hdfs-site.xml</b> called <b>ns-fed</b> which points to two of the routers:</p>
<p>In order for the system to support data-locality, you must configure your NameNodes so that they will trust the routers to supply the user’s client IP address. <code>dfs.namenode.ip-proxy-users</code> defines a comma separated list of users that are allowed to provide the client ip address via the caller context.</p>
<p>One can add the configurations for Router-based federation to <b>hdfs-rbf-site.xml</b>. The main options are documented in <ahref="../hadoop-hdfs-rbf/hdfs-rbf-default.xml">hdfs-rbf-default.xml</a>. The configuration values are described in this section.</p><section>
<h3><aname="RPC_server"></a>RPC server</h3>
<p>The RPC server to receive connections from the clients.</p>
<tdalign="left"> If true, ConnectionPool will use a new socket when creating a new connection for the same user. And it’s best used with dfs.federation.router.max.concurrency.per.connection together. </td></tr>
<tdalign="left"> The number of server threads for the router to handle RPC requests from admin. </td></tr>
</tbody>
</table></section><section>
<h3><aname="HTTP_Server"></a>HTTP Server</h3>
<p>The HTTP Server to provide Web UI and the HDFS REST interface (<ahref="../hadoop-hdfs/WebHDFS.html">WebHDFS</a>) for the clients. The default URL is “<code>http://router_host:50071</code>”.</p>
<tdalign="left"> Class to resolve files to subclusters. To enable multiple subclusters for a mount point, set to org.apache.hadoop.hdfs.server.federation.resolver.MultipleDestinationMountTableResolver. </td></tr>
<tdalign="left"> If <code>true</code>, the Router gets namenode heartbeats and send to the State Store. If not explicitly specified takes the same value as for <code>dfs.federation.router.heartbeat.enable</code>. </td></tr>
<tdalign="left"> If <code>true</code>, the Router should monitor the namenode in the local machine. </td></tr>
</tbody>
</table>
<p>Note: The config <i>dfs.nameservice.id</i> is recommended to configure if <i>dfs.federation.router.monitor.localnamenode.enable</i> is enabled. This will allow the Router finding the local node directly. Otherwise, it will find the nameservice Id by matching namenode RPC address with the local node address. If multiple addresses are matched, the Router will fail to start. In addition, if the local node is in a HA mode, it is recommend to configure <i>dfs.ha.namenode.id</i>.</p></section><section>
<tdalign="left"> If <code>true</code>, the quota system enabled in the Router. In that case, setting or clearing sub-cluster’s quota directly is not recommended since Router Admin server will override sub-cluster’s quota with global quota.</td></tr>
<tdalign="left"> How often the Router updates quota cache. This setting supports multiple time unit suffixes. If no suffix is specified then milliseconds is assumed. </td></tr>
</tbody>
</table></section><section>
<h3><aname="Security"></a>Security</h3>
<p>Kerberos and Delegation token supported in federation.</p>
<tdalign="left"> The keytab file used by router to login as its service principal. The principal name is configured with ‘dfs.federation.router.kerberos.principal’.</td></tr>
<tdalign="left"> The Router service principal. This is typically set to <aclass="externalLink"href="mailto:router/_HOST@REALM.TLD">router/_HOST@REALM.TLD</a>. Each Router will substitute _HOST with its own fully qualified hostname at startup. The _HOST placeholder allows using the same configuration setting on all Routers in an HA setup. </td></tr>
<tdalign="left"> The hostname for the Router containing this configuration file. Will be different for each machine. Defaults to current hostname. </td></tr>
<tdalign="left"> The server principal used by the Router for web UI SPNEGO authentication when Kerberos security is enabled. This is typically set to <aclass="externalLink"href="mailto:HTTP/_HOST@REALM.TLD">HTTP/_HOST@REALM.TLD</a> The SPNEGO server principal begins with the prefix HTTP/ by convention. If the value is ‘*’, the web server will attempt to login with every principal specified in the keytab file ‘dfs.web.authentication.kerberos.keytab’. </td></tr>
<tdalign="left"> Class to implement state store to delegation tokens. Default implementation uses zookeeper as the backend to store delegation tokens. </td></tr>
</tbody>
</table></section><section>
<h3><aname="Isolation"></a>Isolation</h3>
<p>Isolation and dedicated assignment of RPC handlers across all configured downstream nameservices. The sum of these numbers must be strictly smaller than the total number of router handlers (configed by dfs.federation.router.handler.count).</p>
<tdalign="left"> Default handler allocation model to be used if isolation feature is enabled. Recommend to use <code>org.apache.hadoop.hdfs.server.federation.fairness.StaticRouterRpcFairnessPolicyController</code> to fully use the feature. </td></tr>
<tdalign="left"> Dedicated handler assigned to a specific nameservice. If none is specified equal allocation is done across all nameservices. </td></tr>
<tdalign="left"> Dedicated handler assigned to fan out calls such as <code>renewLease</code>. </td></tr>
</tbody>
</table></section></section><section>
<h2><aname="Metrics"></a>Metrics</h2>
<p>The Router and State Store statistics are exposed in metrics/JMX. These info will be very useful for monitoring. More metrics info can see <ahref="../../hadoop-project-dist/hadoop-common/Metrics.html#RBFMetrics">RBF Metrics</a>, <ahref="../../hadoop-project-dist/hadoop-common/Metrics.html#RouterRPCMetrics">Router RPC Metrics</a> and <ahref="../../hadoop-project-dist/hadoop-common/Metrics.html#StateStoreMetrics">State Store Metrics</a>.</p></section><section>
<p>Enable Router to rename across namespaces. Currently it is implemented based on <ahref="../../hadoop-federation-balance/HDFSFederationBalance.md">HDFS Federation Balance</a> and has some limits comparing with normal rename. 1. It is much slower than the normal rename so need a longer RPC timeout configuration. See <code>ipc.client.rpc-timeout.ms</code> and its description for more information about RPC timeout. 2. It doesn’t support snapshot path. 3. It doesn’t support to rename path with multiple destinations.</p>
<tdalign="left"> Specify the action when rename across namespaces. The option can be NONE(reject rename across namespaces) and DISTCP(rename across namespaces with distcp). </td></tr>
<tdalign="left"> This options has 3 values: trash (move the source path to trash), delete (delete the source path directly) and skip (skip both trash and deletion).</td></tr>