You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by xy...@apache.org on 2023/04/27 20:36:11 UTC

[helix] 02/37: Add listener interfaces and config class (#2249)

This is an automated email from the ASF dual-hosted git repository.

xyuanlu pushed a commit to branch metaclient
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 8629a81441fb54dc809755bdf94b283c57cf5764
Author: xyuanlu <xy...@gmail.com>
AuthorDate: Mon Nov 21 11:32:03 2022 -0800

    Add listener interfaces and config class (#2249)
    
    Add listener interfaces and factory class for metaclient interface
---
 .../apache/helix/metaclient/api/AsyncCallback.java |  36 +-
 .../helix/metaclient/api/ChildChangeListener.java  |  39 +++
 .../metaclient/api/ConnectStateChangeListener.java |  39 +++
 .../helix/metaclient/api/DataChangeListener.java   |  33 ++
 .../metaclient/api/DirectChildChangeListener.java  |  37 ++
 .../metaclient/api/DirectChildSubscribeResult.java |  45 +++
 .../helix/metaclient/api/MetaClientInterface.java  | 371 ++++++++++++++++++---
 .../metaclient/factories/MetaClientConfig.java     | 115 +++++++
 .../metaclient/factories/MetaClientFactory.java    |  37 ++
 .../helix/metaclient/impl/zk/ZkMetaClient.java     | 245 ++++++++++++++
 10 files changed, 954 insertions(+), 43 deletions(-)

diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/AsyncCallback.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/AsyncCallback.java
index 1bfa5c6ab..88ae31623 100644
--- a/meta-client/src/main/java/org/apache/helix/metaclient/api/AsyncCallback.java
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/AsyncCallback.java
@@ -22,28 +22,58 @@ package org.apache.helix.metaclient.api;
 import java.util.List;
 
 /**
- * An asynchronous callback is deferred to invoke after an async CRUD operation returns.
- * The corresponding callback is registered when async CRUD API is invoked.
+ * An asynchronous callback is deferred to invoke after an async CRUD operation finish and return.
+ * The corresponding callback is registered when async CRUD API is invoked. Implementation processes
+ * the result of each CRUD call. It should check return code and perform accordingly.
  */
+// TODO: define return code. failure code should map to MetaClient exceptions.
 public interface AsyncCallback {
   //This callback is used when stat object is returned from the operation.
   interface StatCallback extends AsyncCallback {
+    /**
+     * Process the result of asynchronous calls that returns a stat object.
+     * @param returnCode  The return code of the call.
+     * @param key the key that passed to asynchronous calls.
+     * @param context context object that passed to asynchronous calls.
+     * @param stat the stats of the entry of the given key, returned from the async call.
+     */
     void processResult(int returnCode, String key, Object context, MetaClientInterface.Stat stat);
   }
 
   //This callback is used when data is returned from the operation.
   interface DataCallback extends AsyncCallback {
+    /**
+     * Process the result of asynchronous calls that returns entry data.
+     * @param returnCode  The return code of the call.
+     * @param key The key that passed to asynchronous calls.
+     * @param context context object that passed to asynchronous calls.
+     * @param data returned entry data from the call.
+     * @param stat the stats of the entry of the given key.
+     */
     void processResult(int returnCode, String key, Object context, byte[] data, MetaClientInterface.Stat stat);
   }
 
   //This callback is used when nothing is returned from the operation.
   interface VoidCallback extends AsyncCallback {
+    /**
+     * Process the result of asynchronous calls that has no return value.
+     * @param returnCode  The return code of the call.
+     * @param key he key that passed to asynchronous calls.
+     * @param context context object that passed to asynchronous calls.
+     */
     void processResult(int returnCode, String key, Object context);
   }
 
   //This callback is used to process the list if OpResults from a single transactional call.
   interface TransactionCallback extends AsyncCallback {
-    void processResult(int returnCode, String key, Object context, List<OpResult> opResults);
+    /**
+     * Process the result of asynchronous transactional calls.
+     * @param returnCode  The return code of the transaction call.
+     * @param keys List of keys passed to the async transactional call.
+     * @param context context object that passed to asynchronous calls.
+     * @param opResults The list of transactional results.
+     */
+    void processResult(int returnCode, List<String> keys, Object context, List<OpResult> opResults);
   }
 
 }
\ No newline at end of file
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/ChildChangeListener.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/ChildChangeListener.java
new file mode 100644
index 000000000..4d09450da
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/ChildChangeListener.java
@@ -0,0 +1,39 @@
+package org.apache.helix.metaclient.api;
+
+/*
+ * 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.
+ */
+
+/**
+ * Listener interface for children change events on a particular key. It includes new child
+ * creation, child deletion, child data change.
+ * TODO: add type for persist listener is removed
+ * For hierarchy key spaces like zookeeper, it refers to an entry's entire subtree.
+ * For flat key spaces, it refers to keys that matches `prefix*`.
+ */
+public interface ChildChangeListener {
+  enum ChangeType {
+    ENTRY_CREATED,     // Any child entry created
+    ENTRY_DELETED,     // Any child entry deleted
+    ENTRY_DATA_CHANGE  // Any child entry has value change
+  }
+  /**
+   * Called when any child of the current key has changed.
+   */
+  void handleChildChange(String changedPath, ChangeType changeType) throws Exception;
+}
\ No newline at end of file
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/ConnectStateChangeListener.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/ConnectStateChangeListener.java
new file mode 100644
index 000000000..63e7ae4e8
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/ConnectStateChangeListener.java
@@ -0,0 +1,39 @@
+package org.apache.helix.metaclient.api;
+
+/*
+ * 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.
+ */
+
+public interface ConnectStateChangeListener {
+  /**
+   * Called when the connection state has changed. I
+   * @param prevState previous state before state change event.
+   * @param currentState client state after state change event. If it is a one time listsner, it is
+   *                     possible that the metaclient state changes again
+   */
+  void handleConnectStateChanged(MetaClientInterface.ConnectState prevState, MetaClientInterface.ConnectState currentState) throws Exception;
+
+  /**
+   * Called when new connection failed to established.
+   * @param error error returned from metaclient or metadata service.
+   */
+  void handleConnectionEstablishmentError(final Throwable error) throws Exception;
+
+  // TODO: Consider add a callback for new connection when we add support for session ID.
+
+}
\ No newline at end of file
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/DataChangeListener.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/DataChangeListener.java
new file mode 100644
index 000000000..84d11b34f
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/DataChangeListener.java
@@ -0,0 +1,33 @@
+package org.apache.helix.metaclient.api;
+
+/*
+ * 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.
+ */
+
+/**
+ * Listener interface for events on a particular key, including entry creating, deleting and value change.
+ */
+public interface DataChangeListener {
+  enum ChangeType {
+    ENTRY_CREATED,     // Entry created of the specific path that the listener register to
+    ENTRY_DELETED,     // Entry deleted of the specific path that the listener register to
+    ENTRY_UPDATE      // Entry value updated
+  }
+
+  void handleDataChange(String key, Object data, ChangeType changeType) throws Exception;
+}
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildChangeListener.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildChangeListener.java
new file mode 100644
index 000000000..83e67b8e6
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildChangeListener.java
@@ -0,0 +1,37 @@
+package org.apache.helix.metaclient.api;
+
+/*
+ * 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.
+ */
+
+/**
+ * Listener interface for direct child change event on a particular key. It includes new
+ * child creation or child deletion. The callback won't differentiate these types.
+ * For hierarchy key spaces like zookeeper, it refers to an entry's direct child nodes.
+ * For flat key spaces, it refers to keys that matches `prefix*separator`.
+ */
+public interface DirectChildChangeListener {
+   /**
+    * Called when there is a direct child entry creation or deleted.
+    * @param key The parent key where child change listener is subscribed. It would be the key
+    *            passed to subscribeDirectChildChange.
+    * @throws Exception
+    */
+   void handleDirectChildChange(String key) throws Exception;
+
+}
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildSubscribeResult.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildSubscribeResult.java
new file mode 100644
index 000000000..6d45954b5
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/DirectChildSubscribeResult.java
@@ -0,0 +1,45 @@
+package org.apache.helix.metaclient.api;
+
+/*
+ * 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.
+ */
+
+import java.util.List;
+
+
+public class DirectChildSubscribeResult {
+  // A list of direct children names at the time when change is subscribed.
+  // It includes only one level child name, does not include further sub children names.
+  private final List<String> _children;
+
+  // true means the listener is registered successfully.
+  private final boolean _isRegistered;
+
+  public DirectChildSubscribeResult(List<String> children, boolean isRegistered) {
+    _children = children;
+    _isRegistered = isRegistered;
+  }
+
+  public List<String> getDirectChildren() {
+    return _children;
+  }
+
+  public boolean isRegistered() {
+    return _isRegistered;
+  }
+}
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/api/MetaClientInterface.java b/meta-client/src/main/java/org/apache/helix/metaclient/api/MetaClientInterface.java
index 4ad649c26..5118f6359 100644
--- a/meta-client/src/main/java/org/apache/helix/metaclient/api/MetaClientInterface.java
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/api/MetaClientInterface.java
@@ -20,51 +20,101 @@ package org.apache.helix.metaclient.api;
  */
 
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 
 public interface MetaClientInterface<T> {
 
   enum EntryMode {
-    //The node will be removed automatically when the session associated with the creation
+    // The node will be removed automatically when the session associated with the creation
     // of the node expires.
     EPHEMERAL,
-    //The node will not be automatically deleted upon client's disconnect.
-    PERSISTENT
+
+    // The node will not be automatically deleted upon client's disconnect.
+    // An ephemeral node cannot have sub entry.
+    PERSISTENT,
+
+    // The node will not be automatically deleted when the last sub-entry of the node is deleted.
+    // The node is an ephemeral node.
+    CONTAINER
+  }
+
+  enum ConnectState {
+    // Client is connected to server
+    CONNECTED,
+
+    // Authentication failed.
+    AUTH_FAILED,
+
+    // Server has expired this connection.
+    EXPIRED,
+
+    // When client failed to connect server.
+    INIT_FAILED,
+
+    // When client explicitly call disconnect.
+    CLOSED_BY_CLIENT
   }
 
   /**
    * Interface representing the metadata of an entry. It contains entry type and version number.
+   * TODO: we will add session ID to entry stats in the future
    */
   class Stat {
     private int _version;
     private EntryMode _entryMode;
 
-    public EntryMode getEntryType() {return _entryMode;}
-    public int getVersion() {return  _version;}
+    public EntryMode getEntryType() {
+      return _entryMode;
+    }
+
+    public int getVersion() {
+      return _version;
+    }
   }
 
   //synced CRUD API
-  void create(final String key, T data, final EntryMode mode);
 
-  void create(final String key, T data, final EntryMode mode, long ttl);
+  /**
+   * Create an persistent entry with given key and data. The entry will not be created if there is
+   * an existing entry with the same key.
+   * @param key key to identify the entry
+   * @param data value of the entry
+   */
+  void create(final String key, T data);
+
+  /**
+   * Create an entry of given EntryMode with given key and data. The entry will not be created if
+   * there is an existing entry with ethe same key.
+   * @param key key to identify the entry
+   * @param data value of the entry
+   * @param mode EntryMode identifying if the entry will be deleted upon client disconnect
+   */
+  void create(final String key, final T data, final EntryMode mode);
+
+  // TODO: add TTL create and renew API
 
   /**
    * Set the data for the entry of the given key if it exists and the given version matches the
    * version of the node (if the given version is -1, it matches any node's versions).
+   * @param key key to identify the entry
+   * @param data new data of the entry
+   * @param version expected version of the entry. -1 matched any version.
    */
-  void set(final String key, T data, int version);
+  void set(final String key, final T data, int version);
 
   /**
    * Update existing data of a given key using an updater. This method will issue a read to get
    * current data and apply updater upon the current data.
-   * @param updater : An updater that modifies the entry value.
+   * @param key key to identify the entry
+   * @param updater An updater that modifies the entry value.
    * @return: the updated value.
    */
-  T update(String key, DataUpdater<T> updater);
+  T update(final String key, DataUpdater<T> updater);
 
   /**
    * Check if there is an entry for the given key.
-   * @param key
+   * @param key key to identify the entry
    * @return return a Stat object if the entry exists. Return null otherwise.
    */
   Stat exists(final String key);
@@ -72,87 +122,328 @@ public interface MetaClientInterface<T> {
   /**
    * Fetch the data for a given key.
    * TODO: define exception type when key does not exist
+   * @param key key to identify the entry
+   * @return Return data of the entry
    */
-  T get(String key);
+  T get(final String key);
 
   /**
    * API for transaction. The list of operation will be executed as an atomic operation.
    * @param ops a list of operations. These operations will all be executed or non of them.
-   * @return
+   * @return Return a list of OpResult.
    */
   List<OpResult> transactionOP(final Iterable<Op> ops);
 
   /**
-   * Return a list of sub entries for the given keys
-   * @param path: For metadata storage that has hierarchical key space (e.g. ZK), the path would be
-   *            a parent path,
-   *            For metadata storage that has non-hierarchical key space (e.g. etcd), the path would
-   *            be a prefix path.
+   * Return a list of children for the given keys.
+   * @param key For metadata storage that has hierarchical key space (e.g. ZK), the key would be
+   *            a parent key,
+   *            For metadata storage that has non-hierarchical key space (e.g. etcd), the key would
+   *            be a prefix key.
+   * @eturn Return a list of children keys. Return direct child name only for hierarchical key
+   *        space, return the whole sub key for non-hierarchical key space.
    */
-  List<String> getSubEntryKeys(final String path);
+  List<String> getDirestChildrenKeys(final String key);
 
   /**
-   * Return the number of sub entries for the given keys
-   * @param path: For metadata storage that has hierarchical key space (e.g. ZK), the path would be
-   *            a parent path,
-   *            For metadata storage that has non-hierarchical key space (e.g. etcd), the path would
-   *            be a prefix path.
+   * Return the number of children for the given keys.
+   * @param key For metadata storage that has hierarchical key space (e.g. ZK), the key would be
+   *            a parent key,
+   *            For metadata storage that has non-hierarchical key space (e.g. etcd), the key would
+   *            be a prefix key.
    */
-  int countSubEntries(final String path);
+  int countDirestChildren(final String key);
 
   /**
    * Remove the entry associated with the given key.
    * For metadata storage that has hierarchical key space, the entry can only be deleted if the key
    * has no child entry.
-   * TODO: throws
-   * @param path
-   * @return
+   * TODO: define exception to throw
+   * @param key  key to identify the entry to delete
+   * @return Return true if the deletion is completed
    */
-  boolean delete(String path);
+  boolean delete(final String key);
 
   /**
    * Remove the entry associated with the given key.
    * For metadata storage that has hierarchical key space, remove all its child entries as well
    * For metadata storage that has non-hierarchical key space, this API is the same as delete()
-   * @param path
-   * @return
+   * @param key key to identify the entry to delete
+   * @return Return true if the deletion is completed
    */
-  boolean recursiveDelete(String path);
+  boolean recursiveDelete(final String key);
 
   /* Asynchronous methods return immediately.
    * They take a callback object that will be executed either on successful execution of the request
    * or on error with an appropriate return code indicating the error.
    */
-  void asyncCreate(final String key, T data, int version, long ttl,
+
+  /**
+   * User may register callbacks for async CRUD calls. These callbacks will be executed in a async
+   * thread pool. User could define the thread pool size. Default value is 10.
+   * TODO: add const default value in a separate file
+   * @param poolSize pool size for executing user resisted async callbacks
+   */
+  void setAsyncExecPoolSize(int poolSize);
+
+  /**
+   * The asynchronous version of create.
+   * @param key key to identify the entry
+   * @param data value of the entry
+   * @param mode EntryMode identifying if the entry will be deleted upon client disconnect
+   * @param cb An user defined VoidCallback implementation that will be invoked when async create return.
+   *           @see org.apache.helix.metaclient.api.AsyncCallback.VoidCallback
+   */
+  void asyncCreate(final String key, final T data, final EntryMode mode,
       AsyncCallback.VoidCallback cb);
 
-  void asyncSet(final String key, T data, int version, AsyncCallback.VoidCallback cb);
+  /**
+   * The asynchronous version of set.
+   * @param key key to identify the entry
+   * @param data new data of the entry
+   * @param version expected version if the entry. -1 matched any version
+   * @param cb An user defined VoidCallback implementation that will be invoked when async create return.
+   *           @see org.apache.helix.metaclient.api.AsyncCallback.VoidCallback
+   */
+  void asyncSet(final String key, final T data, final int version, AsyncCallback.VoidCallback cb);
 
-  void asyncUpdate(final String key, DataUpdater<T> updater, AsyncCallback.VoidCallback cb);
+  /**
+   * The asynchronous version of update.
+   * @param key key to identify the entry
+   * @param updater An updater that modifies the entry value.
+   * @param cb An user defined VoidCallback implementation that will be invoked when async create return.
+   *           It will contain the newly updated data if update succeeded.
+   *           @see org.apache.helix.metaclient.api.AsyncCallback.DataCallback
+   */
+  void asyncUpdate(final String key, DataUpdater<T> updater, AsyncCallback.DataCallback cb);
 
+  /**
+   * The asynchronous version of get.
+   * @param key key to identify the entry
+   * @param cb An user defined VoidCallback implementation that will be invoked when async get return.
+   *           It will contain the entry data if get succeeded.
+   *           @see org.apache.helix.metaclient.api.AsyncCallback.DataCallback
+   */
   void asyncGet(final String key, AsyncCallback.DataCallback cb);
 
-  void asyncCountSubEntries(final String path, AsyncCallback.DataCallback cb);
+  /**
+   * The asynchronous version of get sub entries.
+   * @param key key to identify the entry
+   * @param cb An user defined VoidCallback implementation that will be invoked when async count child return.
+   *           It will contain the list of child keys if succeeded.
+   *           @see org.apache.helix.metaclient.api.AsyncCallback.DataCallback
+   */
+  void asyncCountChildren(final String key, AsyncCallback.DataCallback cb);
 
+  /**
+   * The asynchronous version of get sub entries.
+   * @param key key to identify the entry
+   * @param cb An user defined VoidCallback implementation that will be invoked when async exist return.
+   *           It will contain the stats of the entry if succeeded.
+   *           @see org.apache.helix.metaclient.api.AsyncCallback.StatCallback
+   */
   void asyncExist(final String key, AsyncCallback.StatCallback cb);
 
-  void asyncDelete(final String keys, AsyncCallback.VoidCallback cb);
+  /**
+   * The asynchronous version of delete.
+   * @param key key to identify the entry
+   * @param cb An user defined VoidCallback implementation that will be invoked when async delete
+   *           finish and return.  @see org.apache.helix.metaclient.api.AsyncCallback.DataCallback
+   */
+  void asyncDelete(final String key, AsyncCallback.VoidCallback cb);
 
-  void asyncTransaction(final String keys, AsyncCallback.TransactionCallback cb);
+  /**
+   * The asynchronous version of transaction operations.
+   * @param ops A list of operations
+   * @param cb An user defined TransactionCallback implementation that will be invoked when
+   *           transaction operations finish and return. The TransactionCallback will contain
+   *           either a list of OpResult if transaction finish successfully, or a return code
+   *           indicating failure reason. @see org.apache.helix.metaclient.api.AsyncCallback.TransactionCallback
+   */
+  void asyncTransaction(final Iterable<Op> ops, AsyncCallback.TransactionCallback cb);
 
   /* Batched APIs return result to user when all request finishes.
    * These calls are not executed as a transaction.
    */
-  boolean[] create(List<String> key, List<T> data, List<EntryMode> mode, List<Long> ttl);
 
-  boolean[] set(List<String> keys, List<T> values, List<Integer> version);
+  /**
+   * Batch version of create. All entries will be created in persist mode. Returns when all request
+   * finishes. These calls are not executed as a transaction.
+   * @param key A list of key for create operations.
+   * @param data A list of data. Need to be in the same length of list of key.
+   * @return A list of boolean indicating create result of each operation.
+   */
+  boolean[] create(List<String> key, List<T> data);
+
+  /**
+   * Batch version of create. Returns when all request finishes. These calls are not executed as a
+   * transaction.
+   * @param key A list of key for create operations.
+   * @param data A list of data. Need to be in the same length of list of key.
+   * @param mode A list of EntryMode. Need to be in the same length of list of key.
+   * @return A list of boolean indicating create result of each operation.
+   */
+  boolean[] create(List<String> key, List<T> data, List<EntryMode> mode);
+
+  /**
+   * Batch version of set. Returns when all request finishes. These calls are not executed as a
+   * transaction.
+   * @param keys A list of key for set operations.
+   * @param datas A list of data. Need to be in the same length of list of key.
+   * @param version A list of expected version of the entry. -1 matched any version.
+   *                Need to be in the same length of list of key.
+   * @return A list of boolean indicating set result of each operation.
+   */
+  boolean[] set(List<String> keys, List<T> datas, List<Integer> version);
 
+  /**
+   * Batch version of update. Returns when all request finishes. These calls are not executed as a
+   * transaction.
+   * @param keys A list of key for update operations.
+   * @param updater A list of updater. Need to be in the same length of list of key.
+   * @return A list of updated entry values.
+   */
   List<T> update(List<String> keys, List<DataUpdater<T>> updater);
 
+  /**
+   * Batch version of get. Returns when all request finishes. These calls are not executed as a
+   * transaction.
+   * @param keys A list of key for get operations.
+   * @return A list of entry values.
+   */
   List<T> get(List<String> keys);
 
+  /**
+   * Batch version of exists. Returns when all request finishes. These calls are not executed as a
+   * transaction.
+   * @param keys A list of key for exists operations.
+   * @return A list of stats for the given entries.
+   */
   List<Stat> exists(List<String> keys);
 
+  /**
+   * Batch version of delete. Returns when all request finishes. These calls are not executed as a
+   * transaction.
+   * @param keys A list of key for delete operations.
+   * @return A list of boolean indicating delete result of each operation.
+   */
   boolean[] delete(List<String> keys);
 
+  /**
+   * Maintains a connection with underlying metadata service based on config params. Connection
+   * created by this method will be used to perform CRUD operations on metadata service.
+   * @return True if connection is successfully established.
+   */
+  boolean connect();
+
+  /**
+   * Disconnect from server explicitly.
+   */
+  void disconnect();
+
+  /**
+   * @return client current connection state with metadata service.
+   */
+  ConnectState getClientConnectionState();
+
+  // Event notification APIs, user can register multiple listeners on the same key/connection state.
+  // All listeners will be automatically removed when client is disconnected.
+  // TODO: add auto re-register listener option
+
+  /**
+   * Subscribe change of a particular entry. Including entry data change, entry deletion and creation
+   * of the given key.
+   * @param key Key to identify the entry
+   * @param listener An implementation of DataChangeListener
+   *                 @see org.apache.helix.metaclient.api.DataChangeListener
+   * @param skipWatchingNonExistNode Will not register lister to an non-exist key if set to true.
+   *                                 Please set to false if you are expecting ENTRY_CREATED type.
+   * @param persistListener The listener will persist when set to true. Otherwise it will be a one
+   *                        time triggered listener.
+   * @return Return an boolean indication if subscribe succeeded.
+   */
+  boolean subscribeDataChange(String key, DataChangeListener listener,
+       boolean skipWatchingNonExistNode, boolean persistListener);
+
+  /**
+   * Subscribe for direct child change event on a particular key. It includes new child
+   * creation or deletion. It does not include existing child data change.
+   * For hierarchy key spaces like zookeeper, it refers to an entry's direct children nodes.
+   * For flat key spaces, it refers to keys that matches `prefix*separator`.
+   * @param key key to identify the entry.
+   * @param listener An implementation of DirectSubEntryChangeListener.
+   *                 @see org.apache.helix.metaclient.api.DirectChildChangeListener
+   * @param skipWatchingNonExistNode If the passed in key does not exist, no listener wil be registered.
+   * @param persistListener The listener will persist when set to true. Otherwise it will be a one
+   *                        time triggered listener.
+   *
+   * @return Return an DirectSubEntrySubscribeResult. It will contain a list of direct sub children if
+   *         subscribe succeeded.
+   */
+  DirectChildSubscribeResult subscribeDirectChildChange(String key,
+      DirectChildChangeListener listener, boolean skipWatchingNonExistNode,
+      boolean persistListener);
+
+  /**
+   * Subscribe for connection state change.
+   * @param listener An implementation of ConnectStateChangeListener.
+   *                 @see org.apache.helix.metaclient.api.ConnectStateChangeListener
+   * @param persistListener The listener will persist when set to true. Otherwise it will be a one
+   *                        time triggered listener.
+   *
+   * @return Return an boolean indication if subscribe succeeded.
+   */
+  boolean subscribeStateChanges(ConnectStateChangeListener listener, boolean persistListener);
+
+  /**
+   * Subscribe change for all children including entry change and data change.
+   * For hierarchy key spaces like zookeeper, it would watch the whole tree structure.
+   * For flat key spaces, it would watch for keys with certain prefix.
+   * @param key key to identify the entry.
+   * @param listener An implementation of ChildChangeListener.
+   *                 @see org.apache.helix.metaclient.api.ChildChangeListener
+   * @param skipWatchingNonExistNode If the passed in key does not exist, no listener wil be registered.
+   * @param persistListener The listener will persist when set to true. Otherwise it will be a one
+   *                        time triggered listener.
+   */
+  boolean subscribeChildChanges(String key, ChildChangeListener listener,
+      boolean skipWatchingNonExistNode, boolean persistListener);
+
+  /**
+   * Unsubscribe the listener to further changes. No-op if the listener is not subscribed to the key.
+   * @param key Key to identify the entry.
+   * @param listener The listener to unsubscribe.
+   */
+  void unsubscribeDataChange(String key, DataChangeListener listener);
+
+  /**
+   * Unsubscribe the listener to further changes. No-op if the listener is not subscribed to the key.
+   * @param key Key to identify the entry.
+   * @param listener The listener to unsubscribe.
+   */
+  void unsubscribeDirectChildChange(String key, DirectChildChangeListener listener);
+
+  /**
+   * Unsubscribe the listener to further changes. No-op if the listener is not subscribed to the key.
+   * @param key Key to identify the entry.
+   * @param listener The listener to unsubscribe.
+   */
+  void unsubscribeChildChanges(String key, ChildChangeListener listener);
+
+  /**
+   * Unsubscribe the listener to further changes. No-op if the listener is not subscribed to the key.
+   * @param listener The listener to unsubscribe.
+   */
+  void unsubscribeConnectStateChanges(ConnectStateChangeListener listener);
+
+  /**
+   * Block the call until the given key exists or timeout.
+   * @param key Key to monitor.
+   * @param timeUnit timeout unit
+   * @param timeOut timeout value
+   * @return
+   */
+  boolean waitUntilExists(String key, TimeUnit timeUnit, long timeOut);
+
+  // TODO: Secure CRUD APIs
 }
\ No newline at end of file
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientConfig.java b/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientConfig.java
new file mode 100644
index 000000000..f317fd923
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientConfig.java
@@ -0,0 +1,115 @@
+package org.apache.helix.metaclient.factories;
+
+/*
+ * 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.
+ */
+
+class MetaClientConfig {
+
+  public enum StoreType {
+    ZOOKEEPER, ETCD, CUSTOMIZED
+  }
+
+  private final String _connectionAddress;
+  private final long _connectionTimeout;
+  private final boolean _enableAuth;
+  private final StoreType _storeType;
+
+  public String getConnectionAddress() {
+    return _connectionAddress;
+  }
+
+  public long getConnectionTimeout() {
+    return _connectionTimeout;
+  }
+
+  public boolean isAuthEnabled() {
+    return _enableAuth;
+  }
+
+  public StoreType getStoreType() {
+    return _storeType;
+  }
+
+  // TODO: More options to add later
+  // private boolean _autoReRegistWatcher;  // re-register one time watcher when set to true
+  // private boolean _resetWatchWhenReConnect; // re-register previous existing watcher when reconnect
+  //
+  //  public enum RetryProtocol {
+  //    NO_RETRY, EXP_BACK_OFF, CONST_RETRY_INTERVAL
+  //  }
+  //  private RetryProtocol _retryProtocol;
+
+
+  private MetaClientConfig(String connectionAddress, long connectionTimeout, boolean enableAuth,
+      StoreType storeType) {
+    _connectionAddress = connectionAddress;
+    _connectionTimeout = connectionTimeout;
+    _enableAuth = enableAuth;
+    _storeType = storeType;
+  }
+
+  public static class Builder {
+    private String _connectionAddress;
+
+    private long _connectionTimeout;
+    private boolean _enableAuth;
+    //private RetryProtocol _retryProtocol;
+    private StoreType _storeType;
+
+
+
+    public MetaClientConfig build() {
+      validate();
+      return new MetaClientConfig(_connectionAddress, _connectionTimeout, _enableAuth, _storeType);
+    }
+
+    public Builder() {
+      // set default values
+      setAuthEnabled(false);
+      setConnectionTimeout(-1);
+    }
+
+    public Builder setConnectionAddress(String connectionAddress) {
+      _connectionAddress = connectionAddress;
+      return this;
+    }
+
+    public Builder setAuthEnabled(Boolean enableAuth) {
+      _enableAuth = enableAuth;
+      return this;
+    }
+
+    public Builder setConnectionTimeout(long timeout) {
+      _connectionTimeout = timeout;
+      return this;
+    }
+
+    public Builder setStoreType(StoreType storeType) {
+      _storeType = storeType;
+      return this;
+    }
+
+    private void validate() {
+      if (_storeType == null || _connectionAddress == null) {
+        throw new IllegalArgumentException(
+            "MetaClientConfig.Builder: store type or connection string is null");
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientFactory.java b/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientFactory.java
new file mode 100644
index 000000000..cf3feb0ef
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/factories/MetaClientFactory.java
@@ -0,0 +1,37 @@
+package org.apache.helix.metaclient.factories;
+
+/*
+ * 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.
+ */
+
+
+import org.apache.helix.metaclient.api.MetaClientInterface;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A factory class for MetaClient. It returns MetaClient entity based on config.
+ */
+public class MetaClientFactory {
+  private static final Logger LOG = LoggerFactory.getLogger(MetaClientFactory.class);
+
+  public MetaClientInterface getMetaClient(MetaClientConfig config) {
+    return null;
+  }
+}
diff --git a/meta-client/src/main/java/org/apache/helix/metaclient/impl/zk/ZkMetaClient.java b/meta-client/src/main/java/org/apache/helix/metaclient/impl/zk/ZkMetaClient.java
new file mode 100644
index 000000000..8437e387f
--- /dev/null
+++ b/meta-client/src/main/java/org/apache/helix/metaclient/impl/zk/ZkMetaClient.java
@@ -0,0 +1,245 @@
+package org.apache.helix.metaclient.impl.zk;
+
+/*
+ * 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.
+ */
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.helix.metaclient.api.AsyncCallback;
+import org.apache.helix.metaclient.api.ChildChangeListener;
+import org.apache.helix.metaclient.api.ConnectStateChangeListener;
+import org.apache.helix.metaclient.api.DataChangeListener;
+import org.apache.helix.metaclient.api.DataUpdater;
+import org.apache.helix.metaclient.api.DirectChildChangeListener;
+import org.apache.helix.metaclient.api.DirectChildSubscribeResult;
+import org.apache.helix.metaclient.api.MetaClientInterface;
+import org.apache.helix.metaclient.api.OpResult;
+import org.apache.helix.zookeeper.api.client.RealmAwareZkClient;
+
+
+public class ZkMetaClient implements MetaClientInterface {
+
+  private RealmAwareZkClient _zkClient;
+
+  public ZkMetaClient() {
+
+  }
+
+  @Override
+  public void create(String key, Object data) {
+
+  }
+
+  @Override
+  public void create(String key, Object data, EntryMode mode) {
+
+  }
+
+  @Override
+  public void set(String key, Object data, int version) {
+
+  }
+
+  @Override
+  public Object update(String key, DataUpdater updater) {
+    return null;
+  }
+
+  @Override
+  public Stat exists(String key) {
+    return null;
+  }
+
+  @Override
+  public Object get(String key) {
+    return null;
+  }
+
+  @Override
+  public List<String> getDirestChildrenKeys(String key) {
+    return null;
+  }
+
+  @Override
+  public int countDirestChildren(String key) {
+    return 0;
+  }
+
+  @Override
+  public boolean delete(String key) {
+    return false;
+  }
+
+  @Override
+  public boolean recursiveDelete(String key) {
+    return false;
+  }
+
+  @Override
+  public void setAsyncExecPoolSize(int poolSize) {
+
+  }
+
+  @Override
+  public void asyncCreate(String key, Object data, EntryMode mode, AsyncCallback.VoidCallback cb) {
+
+  }
+
+  @Override
+  public void asyncSet(String key, Object data, int version, AsyncCallback.VoidCallback cb) {
+
+  }
+
+  @Override
+  public void asyncUpdate(String key, DataUpdater updater, AsyncCallback.DataCallback cb) {
+
+  }
+
+  @Override
+  public void asyncGet(String key, AsyncCallback.DataCallback cb) {
+
+  }
+
+  @Override
+  public void asyncCountChildren(String key, AsyncCallback.DataCallback cb) {
+
+  }
+
+  @Override
+  public void asyncExist(String key, AsyncCallback.StatCallback cb) {
+
+  }
+
+  @Override
+  public void asyncDelete(String keys, AsyncCallback.VoidCallback cb) {
+
+  }
+
+  @Override
+  public boolean[] create(List key, List data, List mode) {
+    return new boolean[0];
+  }
+
+  @Override
+  public boolean[] create(List key, List data) {
+    return new boolean[0];
+  }
+
+  @Override
+  public void asyncTransaction(Iterable iterable, AsyncCallback.TransactionCallback cb) {
+
+  }
+
+
+  @Override
+  public boolean connect() {
+    return false;
+  }
+
+  @Override
+  public void disconnect() {
+
+  }
+
+  @Override
+  public ConnectState getClientConnectionState() {
+    return null;
+  }
+
+  @Override
+  public boolean subscribeDataChange(String key, DataChangeListener listener,
+      boolean skipWatchingNonExistNode, boolean persistListener) {
+    return false;
+  }
+
+  @Override
+  public DirectChildSubscribeResult subscribeDirectChildChange(String key,
+      DirectChildChangeListener listener, boolean skipWatchingNonExistNode,
+      boolean persistListener) {
+    return null;
+  }
+
+  @Override
+  public boolean subscribeStateChanges(ConnectStateChangeListener listener,
+      boolean persistListener) {
+    return false;
+  }
+
+  @Override
+  public boolean subscribeChildChanges(String key, ChildChangeListener listener,
+      boolean skipWatchingNonExistNode, boolean persistListener) {
+    return false;
+  }
+
+  @Override
+  public void unsubscribeDataChange(String key, DataChangeListener listener) {
+
+  }
+
+  @Override
+  public void unsubscribeDirectChildChange(String key, DirectChildChangeListener listener) {
+
+  }
+
+  @Override
+  public void unsubscribeChildChanges(String key, ChildChangeListener listener) {
+
+  }
+
+  @Override
+  public void unsubscribeConnectStateChanges(ConnectStateChangeListener listener) {
+
+  }
+
+  @Override
+  public boolean waitUntilExists(String key, TimeUnit timeUnit, long time) {
+    return false;
+  }
+
+  @Override
+  public boolean[] delete(List keys) {
+    return new boolean[0];
+  }
+
+  @Override
+  public List<Stat> exists(List keys) {
+    return null;
+  }
+
+  @Override
+  public List get(List keys) {
+    return null;
+  }
+
+  @Override
+  public List update(List keys, List updater) {
+    return null;
+  }
+
+  @Override
+  public boolean[] set(List keys, List values, List version) {
+    return new boolean[0];
+  }
+
+  @Override
+  public List<OpResult> transactionOP(Iterable iterable) {
+    return null;
+  }
+}