You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by jx...@apache.org on 2020/04/22 21:52:07 UTC

[helix] branch distributed-lock updated (c11ac95 -> 9b2d799)

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

jxue pushed a change to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git.


 discard c11ac95  Rename interface HelixLock to DistributedLock
 discard 1dc7e59  Remove dependency of LockInfo on HelixProperty
 discard 5984267  Clean up code
 discard 62a0c4b  Created LockScope interface
 discard ed3b444  refactor LockInfo and some updates on the HelixLockScope
 discard 2a7d5ee  Added cluster level to HelixLockScope and convert lock path to uppercase
 discard f1d7b31  simplified acquireLock logic
 discard 1038473  Fixed lock path generation
 discard 33bceca  Changed method doc for releaseLock in HelixLock interface
 discard 6868eb6  A few fixes on syntax
 discard ba3bec9  Added test to acquire lock simultaneously
 discard 32f3b86  Fixed logic of release and isOwner
 discard d58fbe6  Added unit tests for Helix nonblocking lock
 discard 86c4b30  created Helix nonblocking lock based on zk
 discard e01c8d5  Added details in comments
 discard df5b89c  Added LockInfo interface
 discard f0a3bb2  Created Helix distributed lock design (apache#702)
 discard 5bea978  Created Helix distributed lock interface (#703)
 discard ec6bd29  Add Helix Distributed lock module (#673)
     add 2d9e824  Fix handleNewSession creating ephemeral node with expired session (#642)
     add 72edf48  Add method to wait and return established session's ID (#677)
     add b969f38  Set helix style to only wrap if long for throws. (#634)
     add 0947572  [maven-release-plugin] prepare release helix-0.9.2
     add 20218b9  Revert "[maven-release-plugin] prepare release helix-0.9.2"
     add b37c828  [maven-release-plugin] prepare release helix-0.9.3
     add 918d426  [maven-release-plugin] prepare for next development iteration
     add 1560130  Add ZooKeeperAccessor to helix-rest (#671)
     add ceed688  Revert "[maven-release-plugin] prepare for next development iteration"
     add d13eaed  Revert "[maven-release-plugin] prepare release helix-0.9.3"
     add 662dc0f  Enable helix-front for release
     add f8d201c  Call session aware createEphemeral to create live instance. (#700)
     add e2b377a  Disable helix-front
     add d294499  Force Maven to use HTTPS in top level pom.xml
     add e5c2f77  Removed useless znodes to resolve dependency between test methods in TestZKUtil (#693)
     add 48cabc5  Bump jackson-databind from 2.9.5 to 2.9.10.1 in /helix-rest (#597)
     add db2e373  Integration test for controller connect and disconnect (#681)
     add ecf6bb8  Add workflow garbage collector  (#705)
     add bee3ed2  Merge Waged rebalancer branch code to master. (#724)
     add 2676f13  Revert "Merge Waged rebalancer branch code to master. (#724)"
     add aed6d7e  Define the WAGED rebalancer interfaces.
     add 786d238  Adding the configuration items of the WAGED rebalancer. (#348)
     add 253851a  Implement the WAGED rebalancer cluster model (#362)
     add 41aee1b  Change the rebalancer assignment record to be ResourceAssignment instead of IdealState. (#398)
     add d4d4941  Convert all the internal assignment state objects to be ResourceAssignment. (#399)
     add 28b65aa  Implement Cluster Model Provider. (#392)
     add f93aeb5  Add cluster level default instance config. (#413)
     add 4372acf  Add ChangeDetector interface and ResourceChangeDetector implementation (#388)
     add bc6d6f0  Redefine the hard/soft constraints (#422)
     add 73e8336  Refine the WAGED rebalancer related interfaces for integration (#431)
     add 3995879  Revert "Refine the WAGED rebalancer related interfaces for integration (#431)" (#437)
     add b273df4  Resubmit the change: Refine the WAGED rebalancer related interfaces for integration (#431)
     add 8cbfdce  Modify the expected change type from CONFIG to CLUSTER_CONFIG in the WAGED rebalancer. (#438)
     add ff82c34  Add special treatment for ClusterConfig
     add 27630a9  Record the replica objects in the AssignableNode in addition to the partition name (#440)
     add eb437c9  Add BucketDataAccessor for large writes
     add cf8520a  Implement the basic constraint based algorithm (#381)
     add 0895c0d  Validate the instance capacity/partition weight configuration while constructing the assignable instances (#451)
     add 34a2cf8  Implement the WAGED rebalancer with the limited functionality. (#443)
     add dd80b9e  HardConstraints Implementation and unit tests (#433)
     add a4010e2  Implement AssignmentMetadataStore (#453)
     add 63b45ff  Fix TestWagedRebalancer and add constructor in AssignmentMetadataStore
     add cf7b7a5  Implement one of the soft constraints (#450)
     add e49d26b  Add soft constraint: ResourcetopStateAntiAffinityConstraint (#465)
     add 536062d  Implement MaxCapacityUsageInstanceConstraint soft constraint (#463)
     add a07296a  Add soft constraint: ResourcePartitionAntiAffinityConstraint (#464)
     add de800d7  Improve ResourceTopStateAntiAffinityConstraint (#475)
     add 7a34f2c  Adjust the expected replica count according to fault zone count. (#476)
     add d2ffd00  PartitionMovementSoftConstraint Implementation (#474)
     add dcc4862  Add the remaining implementation of ConstraintBasedAlgorithmFactory (#478)
     add 3e0554b  Integrate the WAGED rebalancer with all the related components. (#466)
     add 7bb7f57  Separate AssignableNode properties by Immutable and Mutable (#485)
     add 71948ec  Add delayed rebalance and user-defined preference list features to the WAGED rebalancer. (#456)
     add 95a55d6  Enable maintenance mode for the WAGED rebalancer.
     add 43a9bce  Adjust the topology processing logic for instance to ensure backward compatibility.
     add 081b3f2  Load soft constraint weight from resources/properties file (#492)
     add 359d6de  Add latency metric components for WAGED rebalancer (#490)
     add b287fb5  Fixing rebalance cache issue and stablize the tests. (#510)
     add a318890  More strict partition weight validation while creating the cluster model. (#511)
     add 08bc164  Increase parallelism for ZkBucketDataAccessor (#506)
     add bc2d0cf  The WAGED rebalancer returns the previously calculated assignment on calculation failure (#514)
     add 2d1e8cf  Make log clearer after finishing calculateAssignment. (#531)
     add 285318a  Implement monitoring mbeans for the WAGED rebalancer. (#525)
     add afb19a1  Refine the rebalance scope calculating logic in the WAGED rebalancer. (#519)
     add b63a895  Make WagedRebalancer static by creating a ThreadLocal (#540)
     add f48f4d2  Change change detector to a regular field in the WAGED rebalancer instead of static threadlocal. (#543)
     add 3f237df  Refactor soft constraints to simply the algorithm and fix potential issues. (#520)
     add cd3de5a  Minor fix for the constraints related tests. (#545)
     add dcda863  Adjust the replica rebalance calculating ordering to avoid static order. (#535)
     add e27c6fa  Implement increment() method in CountMetric class. (#537)
     add f7df84c  Modify the ivy file to add the new math3 lib dependency. (#546)
     add 6e37673  Fix a missing parameter when the WAGED rebalancer init the change detector. (#547)
     add 76e0fc0  Add the new Rebalancer monitor domain to the active domain list. (#550)
     add dc40825  Refine ivy file config. The org were not configured correctly. (#551)
     add e5e3689  Use a deep copy of the new best possible assignment for measuring baseline divergence. (#542)
     add 2b3f9fa  Add max capacity usage metric for instance monitor. (#548)
     add c2b7ac7  Fix formula incorrection in the comment for measuring baseline divergence. (#559)
     add ae22186  Avoid redundant writes in AssignmentMetadataStore (#564)
     add 5691d3a  Filter resource map with ideal states for instance capacity metrics. (#574)
     add 0b1b337  Introduce Dry-run Waged Rebalancer for the verifiers and tests. (#573)
     add e93e050  Change ClusterConfig.setDefaultCapacityMap to be private. (#590)
     add 8a8478a  Add Java API for adding and validating resources for WAGED rebalancer (#570)
     add edc8a86  Change calculation for baseline divergence. (#598)
     add 435360f  Improve the WAGED rebalancer performance. (#586)
     add f4b67ae  Fix the unstable test TestZeroReplicaAvoidance. (#603)
     add 37fd496  Add REST API endpoints for WAGED Rebalancer (#611)
     add 6dbc725  Fix a potential issue in the ResourceChangeSnapshot. (#635)
     add 4c48d02  Simply and enhance the RebalanceLatencyGauge so it can be used in multi-threads. (#636)
     add 30695f0  Add new WAGED rebalancer config item "GLOBAL_REBALANCE_ASYNC_MODE". (#637)
     add a2771cb  Decouple the event type and the scheduled rebalance cache refresh option. (#638)
     add 75904ef  Improve the algorithm so it prioritizes the assignment to the idle nodes when the constraint evaluation results are the same (#651)
     add 39f3959  Refine the WAGED rebalancer to minimize the partial rebalance workload. (#639)
     add adfaf4e  Refine methods name and comments. (#664)
     add 6b73653  Asynchronously calculating the Baseline (#632)
     add fe4ecb8  Reorgnize the test case so the new WAGED expand cluster tests are not skipped. (#670)
     add a1557df  Fix the Helix rest tests by cleaning up the environment before testing. (#679)
     add feb2562  Add instance capacity gauge (#557)
     add eb40e15  Add resource partition weight gauge (#686)
     add 71c79fd  Add WAGED rebalancer reset method to clean up cached status. (#696)
     add ec3568d  Reset the WAGED rebalancer once the controller newly acquires leadership. (#690)
     add 85f246a  Fix the watcher leakage issue (#688)
     add 637e943  Fix the unstable test TestWagedRebalance.testRebalancerReset. (#735)
     add 2ba469d  Change the WAGED rebalancer missing error log to warning. (#740)
     add b227b3b  Add metrics-common, zookeeper-api, helix-common modules (#684)
     add afc62ab  Fix the flaky test (#749)
     add 12f11a3  Fix ConcurrentModification exception in Workflow Garbage Collection (#741)
     add 19811ea  Remove workflow garbage collection (#803)
     add 98a4be3  Fix the ConcurrentModificationException in ClusterEvent.java (#785)
     add 0f3c64b  Add system property options to config write size limit for ZNRecord Serializer (#809)
     add 496d573  Async write operation should not throw Exception for serializing error (#845)
     add 1747ac0  Fix type cast in TestRawZkClient (#853)
     add 89dfe91  Add logs and throw exceptions in getInstanceById (#858)
     add 3e264f0  Downgrade the log level to INFO when isInstanceSetup() fails. (#870)
     add fcf78cd  Generate cancellation message for currentState=null desiredState=DROPPED (#831)
     add 5de66f2  Update bump-up.command with metrics-common
     add 5dd01c0  Fix testInstancesStoppable_zoneBased (#880)
     add 26da7bf  Change helix-core's ivy import for commons-math3
     add e5c2a23  Migrate the IdealState usage to read Resource Config for the delayed rebalance. (#878)
     add 1651f9e  Standardize the logging message format in ClusterAccessor and ConfigAccessor (#886)
     add ecae243  Add REST APIs for get, set, update RestConfig (#849)
     add c966784  Remove possible score-tie between two AssignableNodes (#889)
     add f96a3c4  Support enableCompression in workflow and job configs (#883)
     add d109c64  Fix the concurrent modification error happens during the HelixManager initHandlers() call (#904)
     add 5e529ad  Fix TestZNRecordSerializeWriteSizeLimit (#911)
     add 4355430  Refresh live instance while fetching the current state information in the RoutingTableProvider. (#920)
     add 184a50a  Fix the scheduling decision for multiple currentStates (#923)
     add ba0f4e5  Add CloudConfig code
     add 64ca238  add Helix cloud interface and implementation skeleton methods
     add 9f6cc4b  Add java API to create cluster with CloudConfig
     add 3bbeb31  Add REST API for Cluster Creation with CloudConfig (#675)
     add 5dac1cc  Add Helix properties factory and class (#653)
     add 079483b  Implement Azure cloud instance information processor (#698)
     add c2f0dc3  Modify participant manager to add cluster auto registration logic (#695)
     add a32f387  add one more test for auto registration (#806)
     add 912e794  Change the cluster creation logic (#872)
     add 7f640e4  Add construction of domain in Helix participant logic (#876)
     add ab3c4da  Change the REST call for delete CloudConfig  (#882)
     add ec2f3d9  Add REST and JAVA API to modify existing cloudconfig (#898)
     add c2f3c08  Fix ClusterAccessor::createCluster wrt CloudConfig (#937)
     add adebe17  Add MetadataStoreRoutingData interface and TrieRoutingData class to helix-rest
     add 1323b25  Enable two test runs with multiZk system property (#710)
     add 2eaf882  Upgrade AbstractTestClass with multi-ZK support in helix-rest (#717)
     add c967c7c  Upgrade ZkTestBase with multi-ZK support in helix-core (#712)
     add d5a042d  Add MockMetadataStoreDirectoryServer (#719)
     add a4edbd2  Add MetadataStoreRoutingDataReader interface and ZkRoutingDataReader class to helix-rest (#714)
     add 08949f8  Add MetadataStoreDirectory and ZkMetadataStoreDirectory (#720)
     add 15cc55e  Add TrieRoutingData constructor (#731)
     add 661729b  Add MetadataStoreRoutingDataWriter with DistributedLeaderElection (#727)
     add c6504f3  Add REST read endpoints to Helix Rest to provide resource access to metadata store directory (#744)
     add 527c215  Rebase ZooScalability from upstream master
     add ab4f41e  Implement getAllMappingUnderPath and getMetadataStoreRealm in ZkMetadataStoreDirectory
     add e960917  Add write REST endpoints to helix rest for metadata store directory (#757)
     add 7512cfd  Add RealmAwareZkClient and RealmAwareZkClientFactory interfaces (#745)
     add c990679  Add validation logic to MSD write operations (#759)
     add 01ae06a  Create metadata-store-directory-common module (#771)
     add 6e4f27d  Fix tests in apache/zooscalability and rebase from apache/master (#787)
     add 642ee20  Add DedicatedZkClient and update DedicatedZkClientFactory (#765)
     add df6453e  Add REST read endpoints to helix-rest for metadata store directory (#761)
     add 65afec0  [helix-rest] Add endpoint to get namespace routing data (#799)
     add 6f9c5c9  Add HttpRoutingDataReader (#775)
     add f180cc9  Add getShardingKeyInPath to MetadataStoreRoutingData (#817)
     add 3af4c1f  Implement request forwarding for ZkRoutingDataWriter (#788)
     add 6b2a6ff  Fix InvalidRoutingData error message in tests (#821)
     add fbddb11  Update bump-up.command and ivy imports (#824)
     add 0980063  Add SharedZkClient/InnerSharedZkClient implementation (#796)
     add c9a9b7e  Improve MetadataStoreDirectoryAccessor endpoints and fix bugs in ZkRoutingDataReader/Writer
     add add11db  Make MSDS endpoint configurable for HttpRoutingDataReader (#836)
     add f645496  Add FederatedZkClient (#789)
     add 7d46f2a  Make ConfigAccessor and ZkUtil realm-aware (#838)
     add 1f0d278  Implement setRoutingData for MetadataStoreDirectoryService (#844)
     add c7ab0f2  Make RealmAwareZkClient implementations use HttpRoutingDataReader for routing data (#819)
     add 45f0989  Instrument ConfigAccessor's constructors (#856)
     add b01e804  Add rerunFailingTestsCount config to surefire-plugin (#865)
     add 7b57e9e  Make ClusterSetup realm-aware (#861)
     add 92aa631  Make ZkCacheBaseDataAccessor and ZkHelixPropertyStore realm-aware (#863)
     add 6e6862b  Make ZkHelixClusterVerifier and its child classes realm-aware (#867)
     add 73ccbc9  Make ZkBaseDataAccessor realm-aware (#855)
     add 289085f  Make ZKHelixAdmin and ZKHelixManager Realm-aware (#846)
     add 56ebbd1  Reformat ZkBaseDataAccessor (#893)
     add ef027a9  Update listClusters() in ZkHelixAdmin (#895)
     add 7184988  Make ZkBucketDataAccessor realm-aware (#894)
     add f90b194  Make ZkUtil realm-aware (#896)
     add 8523baa  Add integration tests for Helix Java APIs (#892)
     add b16ac22  Fix setRoutingData boolean handling; fix leader forwarding url construction (#902)
     add e3f751c  Use Java Generics and inheritance to reduce duplicate code in Helix API Builders (#899)
     add 2d310f3  Make Helix REST realm-aware (#908)
     add b7f6f3c  Make multiZkEnabled configurable in HelixRestNamespace (#915)
     add e303ef2  Fix getClusters() in ZKHelixAdmin for multi-zk mode (#916)
     add 9381a80  Fix MetadataStoreDirectory routing data cache refresh bug (#933)
     add 66c9964  Fix flaky resource accessor tests (#935)
     add 2e9ac43  Change thread pool used for TTL-based GC in ZkBucketDataAccessor (#945)
     add b8f22fd  Add integration test for Helix Java APIs using different MSDS endpoints (#948)
     add 4dcf624  Fix TestCrushAutoRebalanceNonRack failure of dropping instance
     add 4e57f27  Simplify logging
     add 108dfc6  Fix ZkHelixPropertyStore loses Zookeeper notification issue (#924)
     add 64c95ab  Fix unstable test TestRebalancePipeline.testMsgTriggeredRebalance() (#953)
     add 8d6586b  Fix unexpceted partition movements in the CrushEd strategy. (#941)
     add 38b4fa3  Fix Regression on Flaky Tests in TestResourceAccessor (#959)
     new 2417fe6  Add Helix Distributed lock module (#673)
     new 6f4a5bd  Created Helix distributed lock interface (#703)
     new 5930429  Created Helix distributed lock design (apache#702)
     new b243b6d  Added LockInfo interface
     new 0cea09d  Added details in comments
     new 9621a8b  created Helix nonblocking lock based on zk
     new c9bdeab  Added unit tests for Helix nonblocking lock
     new aa4d015  Fixed logic of release and isOwner
     new b53a94c  Added test to acquire lock simultaneously
     new 0dea882  A few fixes on syntax
     new 05d05ce  Changed method doc for releaseLock in HelixLock interface
     new 79ee132  Fixed lock path generation
     new ed53139  simplified acquireLock logic
     new 26abd84  Added cluster level to HelixLockScope and convert lock path to uppercase
     new 80877bf  refactor LockInfo and some updates on the HelixLockScope
     new c899db5  Created LockScope interface
     new 8f9c5c5  Clean up code
     new 89fddf3  Remove dependency of LockInfo on HelixProperty
     new 9b2d799  Rename interface HelixLock to DistributedLock

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (c11ac95)
            \
             N -- N -- N   refs/heads/distributed-lock (9b2d799)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 19 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 bump-up.command                                    |   71 +
 helix-admin-webapp/pom.xml                         |    3 +
 .../org/apache/helix/webapp/HelixAdminWebApp.java  |    2 +-
 .../resources/ClusterRepresentationUtil.java       |    4 +-
 .../helix/webapp/resources/ClusterResource.java    |    4 +-
 .../helix/webapp/resources/ClustersResource.java   |    4 +-
 .../helix/webapp/resources/ConfigResource.java     |    4 +-
 .../helix/webapp/resources/ConstraintResource.java |    4 +-
 .../helix/webapp/resources/ControllerResource.java |    4 +-
 .../resources/ControllerStatusUpdateResource.java  |    2 +-
 .../webapp/resources/CurrentStateResource.java     |    2 +-
 .../webapp/resources/CurrentStatesResource.java    |    2 +-
 .../helix/webapp/resources/ErrorResource.java      |    2 +-
 .../helix/webapp/resources/ErrorsResource.java     |    2 +-
 .../webapp/resources/ExternalViewResource.java     |    2 +-
 .../helix/webapp/resources/IdealStateResource.java |    4 +-
 .../helix/webapp/resources/InstanceResource.java   |    2 +-
 .../helix/webapp/resources/InstancesResource.java  |    4 +-
 .../helix/webapp/resources/JobQueueResource.java   |    4 +-
 .../helix/webapp/resources/JobQueuesResource.java  |    4 +-
 .../apache/helix/webapp/resources/JobResource.java |    4 +-
 .../helix/webapp/resources/JsonParameters.java     |    2 +-
 .../webapp/resources/ResourceGroupResource.java    |    2 +-
 .../webapp/resources/ResourceGroupsResource.java   |    4 +-
 .../helix/webapp/resources/ResourceUtil.java       |    2 +-
 .../webapp/resources/SchedulerTasksResource.java   |    2 +-
 .../helix/webapp/resources/StateModelResource.java |    4 +-
 .../webapp/resources/StateModelsResource.java      |    4 +-
 .../webapp/resources/StatusUpdateResource.java     |    2 +-
 .../webapp/resources/StatusUpdatesResource.java    |    2 +-
 .../helix/webapp/resources/WorkflowsResource.java  |    4 +-
 .../helix/webapp/resources/ZkChildResource.java    |    4 +-
 .../helix/webapp/resources/ZkPathResource.java     |    4 +-
 .../org/apache/helix/webapp/AdminTestBase.java     |    4 +-
 .../org/apache/helix/webapp/AdminTestHelper.java   |    2 +-
 .../helix/webapp/TestClusterManagementWebapp.java  |    2 +-
 .../apache/helix/webapp/TestDisableResource.java   |    2 +-
 .../helix/webapp/TestHelixAdminScenariosRest.java  |    2 +-
 .../helix/webapp/TestResetPartitionState.java      |    2 +-
 .../webapp/resources/TestJobQueuesResource.java    |    2 +-
 helix-common/LICENSE                               |  270 +++
 helix-common/NOTICE                                |   37 +
 .../helix-common-0.9.2-SNAPSHOT.ivy                |   37 +-
 {helix-admin-webapp => helix-common}/pom.xml       |   78 +-
 helix-common/src/assemble/assembly.xml             |   60 +
 .../main/java/org/apache/helix/HelixException.java |    0
 .../java/org/apache/helix/SystemPropertyKeys.java  |   81 +
 .../src/main/java/org/apache/helix/ZNRecord.java   |   30 +-
 .../main/java/org/apache/helix/ZNRecordDelta.java  |   16 +-
 .../zk/serializer/JacksonPayloadSerializer.java    |   15 +-
 .../manager/zk/serializer/PayloadSerializer.java   |   15 +-
 helix-common/src/test/conf/testng.xml              |   27 +
 .../src/test/resources/log4j.properties            |    0
 helix-core/helix-core-0.9.2-SNAPSHOT.ivy           |    2 +-
 helix-core/pom.xml                                 |   23 +-
 .../java/org/apache/helix/BaseDataAccessor.java    |    6 +-
 ...edZkSerializer.java => BucketDataAccessor.java} |   37 +-
 .../main/java/org/apache/helix/ConfigAccessor.java |  225 ++-
 .../main/java/org/apache/helix/GroupCommit.java    |   14 +-
 .../src/main/java/org/apache/helix/HelixAdmin.java |   62 +-
 .../java/org/apache/helix/HelixCloudProperty.java  |  183 ++
 .../java/org/apache/helix/HelixDataAccessor.java   |   11 +-
 .../main/java/org/apache/helix/HelixManager.java   |    2 +
 .../java/org/apache/helix/HelixManagerFactory.java |    2 +-
 .../org/apache/helix/HelixManagerProperty.java     |   74 +
 .../main/java/org/apache/helix/HelixProperty.java  |   20 +-
 .../org/apache/helix/HelixPropertyFactory.java     |   79 +
 ...Exception.java => HelixRebalanceException.java} |   29 +-
 .../main/java/org/apache/helix/HelixTimerTask.java |    5 +
 .../main/java/org/apache/helix/InstanceType.java   |    6 +-
 .../org/apache/helix/LiveInstanceInfoProvider.java |    3 +
 .../main/java/org/apache/helix/PropertyKey.java    |   11 +
 .../java/org/apache/helix/SystemPropertyKeys.java  |   41 -
 .../java/org/apache/helix/ZNRecordAssembler.java   |   29 +-
 .../java/org/apache/helix/ZNRecordBucketizer.java  |   97 +-
 .../java/org/apache/helix/ZNRecordUpdater.java     |   20 +-
 .../cloud/CloudInstanceInformation.java}           |   21 +-
 .../cloud/CloudInstanceInformationProcessor.java}  |   27 +-
 .../apache/helix/api/config/RebalanceConfig.java   |    2 +-
 .../api/config/StateTransitionTimeoutConfig.java   |    2 +-
 .../org/apache/helix/api/listeners/PreFetch.java   |    5 +-
 .../cloud/azure/AzureCloudInstanceInformation.java |   72 +
 .../AzureCloudInstanceInformationProcessor.java    |  160 ++
 .../apache/helix/cloud/azure/AzureConstants.java   |    6 +
 .../constants/CloudProvider.java}                  |    7 +-
 .../apache/helix/common/DedupEventProcessor.java   |    2 +-
 .../apache/helix/common/caches/TaskDataCache.java  |    2 +-
 .../helix/controller/ExternalViewGenerator.java    |    2 +-
 .../helix/controller/GenericHelixController.java   |  131 +-
 .../helix/controller/HelixControllerMain.java      |    2 +-
 .../helix/controller/HierarchicalDataHolder.java   |    2 +-
 .../controller/changedetector/ChangeDetector.java  |   57 +
 .../changedetector/ResourceChangeDetector.java     |  199 +++
 .../changedetector/ResourceChangeSnapshot.java     |  157 ++
 .../ResourceControllerDataProvider.java            |   83 +-
 .../WorkflowControllerDataProvider.java            |    2 +-
 .../controller/rebalancer/AbstractRebalancer.java  |   21 +-
 .../controller/rebalancer/AutoRebalancer.java      |    4 +-
 .../rebalancer/DelayedAutoRebalancer.java          |  208 +--
 .../controller/rebalancer/StatefulRebalancer.java  |   37 +
 .../dataprovider/ZkBasedCapacityProvider.java      |    2 +-
 .../ZkBasedPartitionWeightProvider.java            |    2 +-
 .../AbstractEvenDistributionRebalanceStrategy.java |    2 +-
 .../rebalancer/strategy/AutoRebalanceStrategy.java |    2 +-
 .../strategy/ConstraintRebalanceStrategy.java      |    2 +-
 .../strategy/CrushRebalanceStrategy.java           |    2 +-
 .../strategy/MultiRoundCrushRebalanceStrategy.java |    2 +-
 .../rebalancer/strategy/RebalanceStrategy.java     |    2 +-
 .../rebalancer/util/DelayedRebalanceUtil.java      |  282 +++
 .../rebalancer/util/ResourceUsageCalculator.java   |  192 ++
 .../rebalancer/util/WagedValidationUtil.java       |   91 +
 .../rebalancer/waged/AssignmentMetadataStore.java  |  212 +++
 .../rebalancer/waged/RebalanceAlgorithm.java       |   43 +
 .../rebalancer/waged/WagedRebalancer.java          |  788 +++++++++
 .../constraints/ConstraintBasedAlgorithm.java      |  230 +++
 .../ConstraintBasedAlgorithmFactory.java           |   82 +
 .../constraints/FaultZoneAwareConstraint.java}     |   25 +-
 .../waged/constraints/HardConstraint.java          |   47 +
 .../InstancePartitionsCountConstraint.java         |   41 +
 .../MaxCapacityUsageInstanceConstraint.java        |   42 +
 .../waged/constraints/NodeCapacityConstraint.java  |   50 +
 .../NodeMaxPartitionLimitConstraint.java           |   43 +
 .../constraints/PartitionMovementConstraint.java   |   96 +
 .../constraints/ReplicaActivateConstraint.java}    |   23 +-
 .../ResourcePartitionAntiAffinityConstraint.java   |   43 +
 .../ResourceTopStateAntiAffinityConstraint.java    |   44 +
 .../SamePartitionOnInstanceConstraint.java}        |   24 +-
 .../waged/constraints/SoftConstraint.java          |   90 +
 .../waged/constraints/UsageSoftConstraint.java     |   85 +
 .../constraints/ValidGroupTagConstraint.java}      |   28 +-
 .../rebalancer/waged/model/AssignableNode.java     |  374 ++++
 .../rebalancer/waged/model/AssignableReplica.java  |  161 ++
 .../rebalancer/waged/model/ClusterContext.java     |  172 ++
 .../rebalancer/waged/model/ClusterModel.java       |  132 ++
 .../waged/model/ClusterModelProvider.java          |  507 ++++++
 .../rebalancer/waged/model/OptimalAssignment.java  |   93 +
 .../helix/controller/stages/AttributeName.java     |    3 +-
 .../stages/BestPossibleStateCalcStage.java         |  180 +-
 .../helix/controller/stages/ClusterDataCache.java  |    2 +-
 .../helix/controller/stages/ClusterEvent.java      |    6 +-
 .../stages/CurrentStateComputationStage.java       |   75 +
 .../controller/stages/CurrentStateOutput.java      |   23 +
 .../stages/ExternalViewComputeStage.java           |    7 +-
 .../controller/stages/MessageGenerationPhase.java  |  145 +-
 .../controller/stages/PersistAssignmentStage.java  |    4 +-
 .../stages/TaskGarbageCollectionStage.java         |    1 +
 .../controller/strategy/AutoRebalanceStrategy.java |    2 +-
 .../org/apache/helix/examples/ExampleHelper.java   |    8 +-
 .../helix/examples/IdealStateBuilderExample.java   |    5 +-
 .../apache/helix/examples/IdealStateExample.java   |    5 +-
 .../java/org/apache/helix/examples/Quickstart.java |    7 +-
 .../examples/WeightAwareRebalanceUtilExample.java  |   22 +-
 .../ParticipantHealthReportCollector.java          |    2 +-
 .../ParticipantHealthReportCollectorImpl.java      |    2 +-
 .../healthcheck/ParticipantHealthReportTask.java   |    2 -
 .../apache/helix/manager/zk/BasicZkSerializer.java |   26 +-
 .../helix/manager/zk/ByteArraySerializer.java      |    5 +-
 .../apache/helix/manager/zk/CallbackHandler.java   |   17 +-
 .../helix/manager/zk/ChainedPathZkSerializer.java  |    5 +-
 .../helix/manager/zk/ControllerManagerHelper.java  |    2 +-
 .../helix/manager/zk/CurStateCarryOverUpdater.java |    4 +-
 .../zk/DefaultSchedulerMessageHandlerFactory.java  |    2 +-
 .../manager/zk/DistributedLeaderElection.java      |    2 +-
 .../manager/zk/GenericBaseDataAccessorBuilder.java |  145 ++
 .../helix/manager/zk/GenericZkHelixApiBuilder.java |  139 ++
 .../apache/helix/manager/zk/HelixGroupCommit.java  |    6 +-
 .../helix/manager/zk/ParticipantManager.java       |  240 ++-
 .../helix/manager/zk/PathBasedZkSerializer.java    |   28 +-
 .../apache/helix/manager/zk/WriteThroughCache.java |    2 +-
 .../helix/manager/zk/ZKExceptionHandler.java       |    2 +-
 .../org/apache/helix/manager/zk/ZKHelixAdmin.java  |  510 +++++-
 .../helix/manager/zk/ZKHelixDataAccessor.java      |   12 +-
 .../apache/helix/manager/zk/ZKHelixManager.java    |  217 ++-
 .../java/org/apache/helix/manager/zk/ZKUtil.java   |  123 +-
 ...ializer.java => ZNRecordJacksonSerializer.java} |   22 +-
 .../helix/manager/zk/ZNRecordSerializer.java       |  116 +-
 .../manager/zk/ZNRecordStreamingSerializer.java    |  293 +---
 .../apache/helix/manager/zk/ZkAsyncCallbacks.java  |  176 +-
 .../helix/manager/zk/ZkBaseDataAccessor.java       |  489 ++++--
 .../helix/manager/zk/ZkBucketDataAccessor.java     |  401 +++++
 .../helix/manager/zk/ZkCacheBaseDataAccessor.java  |  122 +-
 .../helix/manager/zk/ZkCacheEventThread.java       |    2 +-
 .../apache/helix/manager/zk/ZkCallbackCache.java   |    6 +-
 .../java/org/apache/helix/manager/zk/ZkClient.java |   15 +-
 .../zk/client/DedicatedZkClientFactory.java        |   50 +-
 .../helix/manager/zk/client/HelixZkClient.java     |  404 +----
 .../manager/zk/client/HelixZkClientFactory.java    |   46 -
 .../helix/manager/zk/client/SharedZkClient.java    |  111 +-
 .../manager/zk/client/SharedZkClientFactory.java   |  102 +-
 .../manager/zk/client/ZkConnectionManager.java     |  120 +-
 .../manager/zk/zookeeper/IZkStateListener.java     |   43 +-
 .../helix/manager/zk/zookeeper/ZkClient.java       | 1832 +-------------------
 .../helix/manager/zk/zookeeper/ZkConnection.java   |  174 +-
 .../org/apache/helix/messaging/ZNRecordRow.java    |    2 +-
 .../handling/HelixStateTransitionHandler.java      |    8 +-
 .../java/org/apache/helix/model/AlertHistory.java  |    2 +-
 .../java/org/apache/helix/model/AlertStatus.java   |    2 +-
 .../main/java/org/apache/helix/model/Alerts.java   |    2 +-
 .../java/org/apache/helix/model/CloudConfig.java   |  265 +++
 .../java/org/apache/helix/model/ClusterConfig.java |  230 ++-
 .../org/apache/helix/model/ClusterConstraints.java |    2 +-
 .../org/apache/helix/model/ControllerHistory.java  |    2 +-
 .../java/org/apache/helix/model/CurrentState.java  |    2 +-
 .../main/java/org/apache/helix/model/Error.java    |    2 +-
 .../java/org/apache/helix/model/ExternalView.java  |    2 +-
 .../java/org/apache/helix/model/HealthStat.java    |    2 +-
 .../org/apache/helix/model/HelixConfigScope.java   |    6 +-
 .../java/org/apache/helix/model/IdealState.java    |    2 +-
 .../org/apache/helix/model/InstanceConfig.java     |   54 +-
 .../org/apache/helix/model/LeaderStandbySMD.java   |    2 +-
 .../java/org/apache/helix/model/LiveInstance.java  |    2 +-
 .../org/apache/helix/model/MaintenanceSignal.java  |    2 +-
 .../org/apache/helix/model/MasterSlaveSMD.java     |    2 +-
 .../main/java/org/apache/helix/model/Message.java  |    2 +-
 .../org/apache/helix/model/OnlineOfflineSMD.java   |    2 +-
 .../org/apache/helix/model/ParticipantHistory.java |    2 +-
 .../java/org/apache/helix/model/PauseSignal.java   |    2 +-
 .../org/apache/helix/model/PersistentStats.java    |    2 +-
 .../java/org/apache/helix/model/RESTConfig.java    |    2 +-
 .../org/apache/helix/model/ResourceAssignment.java |    2 +-
 .../org/apache/helix/model/ResourceConfig.java     |  199 ++-
 .../org/apache/helix/model/ScheduledTaskSMD.java   |    2 +-
 .../apache/helix/model/StateModelDefinition.java   |    7 +-
 .../java/org/apache/helix/model/StatusUpdate.java  |    2 +-
 .../org/apache/helix/model/StorageSchemataSMD.java |    2 +-
 .../main/java/org/apache/helix/model/TaskSMD.java  |    2 +-
 .../model/builder/HelixConfigScopeBuilder.java     |    3 +
 .../helix/model/builder/IdealStateBuilder.java     |    2 +-
 .../helix/monitoring/ZKPathDataDumpTask.java       |    2 +-
 .../monitoring/mbeans/ClusterStatusMonitor.java    |   92 +-
 .../helix/monitoring/mbeans/InstanceMonitor.java   |  192 +-
 .../monitoring/mbeans/InstanceMonitorMBean.java    |   51 -
 .../monitoring/mbeans/MonitorDomainNames.java      |    3 +-
 .../helix/monitoring/mbeans/ResourceMonitor.java   |   99 +-
 .../helix/monitoring/mbeans/ZkClientMonitor.java   |  231 +--
 .../monitoring/mbeans/ZkClientPathMonitor.java     |  228 +--
 .../mbeans/dynamicMBeans/DynamicMBeanProvider.java |   78 +-
 .../mbeans/dynamicMBeans/SimpleDynamicMetric.java  |    2 +-
 .../helix/monitoring/metrics/MetricCollector.java  |   99 ++
 .../metrics/WagedRebalancerMetricCollector.java    |  125 ++
 .../implementation/BaselineDivergenceGauge.java    |   68 +
 .../metrics/implementation/RebalanceCounter.java}  |   23 +-
 .../implementation/RebalanceFailureCount.java}     |   21 +-
 .../implementation/RebalanceLatencyGauge.java      |   89 +
 .../monitoring/metrics/model/CountMetric.java      |   69 +
 .../monitoring/metrics/model/LatencyMetric.java    |   67 +
 .../metrics/model/Metric.java}                     |   29 +-
 .../model/RatioMetric.java}                        |   46 +-
 .../helix/participant/HelixCustomCodeRunner.java   |   26 +-
 .../statemachine/ScheduledTaskStateModel.java      |    2 +-
 .../apache/helix/spectator/RoutingDataCache.java   |   11 +
 .../apache/helix/store/PropertyJsonSerializer.java |    2 +-
 .../apache/helix/store/ZNRecordJsonSerializer.java |    2 +-
 .../helix/store/zk/AutoFallbackPropertyStore.java  |    2 +-
 .../helix/store/zk/ZkHelixPropertyStore.java       |    3 +-
 .../apache/helix/task/AbstractTaskDispatcher.java  |   11 +-
 .../helix/task/DeprecatedTaskRebalancer.java       |    4 +-
 .../main/java/org/apache/helix/task/JobConfig.java |   22 +-
 .../java/org/apache/helix/task/JobContext.java     |    2 +-
 .../java/org/apache/helix/task/JobDispatcher.java  |    2 +-
 .../java/org/apache/helix/task/TaskConstants.java  |    2 +-
 .../java/org/apache/helix/task/TaskDriver.java     |   12 +-
 .../main/java/org/apache/helix/task/TaskUtil.java  |    5 +-
 .../java/org/apache/helix/task/WorkflowConfig.java |   13 +
 .../org/apache/helix/task/WorkflowContext.java     |    2 +-
 .../org/apache/helix/task/WorkflowDispatcher.java  |    2 +-
 .../java/org/apache/helix/task/beans/JobBean.java  |    1 +
 .../org/apache/helix/task/beans/WorkflowBean.java  |    2 +
 .../helix/tools/ClusterExternalViewVerifier.java   |    2 +-
 .../helix/tools/ClusterLiveNodesVerifier.java      |    2 +-
 .../java/org/apache/helix/tools/ClusterSetup.java  |  101 +-
 .../apache/helix/tools/ClusterStateVerifier.java   |   14 +-
 .../org/apache/helix/tools/ClusterVerifier.java    |    8 +-
 .../BestPossibleExternalViewVerifier.java          |  151 +-
 .../ClusterVerifiers/ClusterLiveNodesVerifier.java |   32 +-
 .../StrictMatchExternalViewVerifier.java           |  107 +-
 .../ClusterVerifiers/ZkHelixClusterVerifier.java   |  140 +-
 .../helix/tools/DefaultIdealStateCalculator.java   |    2 +-
 .../tools/IdealCalculatorByConsistentHashing.java  |    2 +-
 .../helix/tools/IdealStateCalculatorByRush.java    |    2 +-
 .../tools/IdealStateCalculatorByShuffling.java     |    2 +-
 .../java/org/apache/helix/tools/MessagePoster.java |    6 +-
 .../helix/tools/StateModelConfigGenerator.java     |    2 +-
 .../java/org/apache/helix/tools/TestExecutor.java  |   10 +-
 .../java/org/apache/helix/tools/TestTrigger.java   |    2 +-
 .../java/org/apache/helix/tools/ZnodeOpArg.java    |    2 +-
 .../java/org/apache/helix/tools/ZnodeValue.java    |    2 +-
 .../tools/commandtools/CurrentStateCleanUp.java    |    4 +-
 .../tools/commandtools/IntegrationTestUtil.java    |    6 +-
 .../helix/tools/commandtools/LocalZKServer.java    |    7 +-
 .../apache/helix/tools/commandtools/ZKDumper.java  |    7 +-
 .../apache/helix/tools/commandtools/ZkCopy.java    |    4 +-
 .../tools/commandtools/ZkLogCSVFormatter.java      |    2 +-
 .../helix/util/ExponentialBackoffStrategy.java     |   52 +-
 .../org/apache/helix/util/GZipCompressionUtil.java |   56 +-
 .../main/java/org/apache/helix/util/HelixUtil.java |   44 +-
 .../apache/helix/util/InstanceValidationUtil.java  |   29 +-
 .../java/org/apache/helix/util/RebalanceUtil.java  |    7 +-
 .../org/apache/helix/util/StatusUpdateUtil.java    |    2 +-
 .../helix/util/WeightAwareRebalanceUtil.java       |    2 +-
 .../java/org/apache/helix/util/ZKClientPool.java   |    2 +-
 .../java/org/apache/helix/util/ZNRecordUtil.java   |    5 +-
 .../src/main/resources/azure-cloud.properties      |   25 +
 .../src/main/resources/helix-manager.properties    |   24 +
 .../resources/soft-constraint-weight.properties    |   26 +
 .../test/java/org/apache/helix/MockAccessor.java   |   37 +-
 .../java/org/apache/helix/TestConfigAccessor.java  |  187 +-
 .../TestEspressoStorageClusterIdealState.java      |   16 +-
 .../java/org/apache/helix/TestGroupCommit.java     |    4 +-
 .../src/test/java/org/apache/helix/TestHelper.java |   99 +-
 .../apache/helix/TestHierarchicalDataStore.java    |    4 +-
 .../org/apache/helix/TestShuffledIdealState.java   |   49 +-
 .../apache/helix/TestZKRoutingInfoProvider.java    |    2 +
 .../test/java/org/apache/helix/TestZkBasis.java    |    8 +-
 .../test/java/org/apache/helix/ZkTestHelper.java   |   49 +-
 .../org/apache/helix/cloud/MockHttpClient.java     |   53 +
 ...TestAzureCloudInstanceInformationProcessor.java |   69 +
 .../java/org/apache/helix/common/ZkTestBase.java   |  105 +-
 .../common/caches/TestCurrentStateSnapshot.java    |    2 +-
 .../changedetector/TestResourceChangeDetector.java |  446 +++++
 .../TestResourceControllerDataProvider.java        |   91 +
 .../rebalancer/TestAutoRebalanceStrategy.java      |    2 +-
 ...stAutoRebalanceStrategyImbalanceAssignment.java |    2 +-
 .../rebalancer/TestZeroReplicaAvoidance.java       |    2 +-
 .../util/TestResourceUsageCalculator.java          |  103 ++
 .../waged/MockAssignmentMetadataStore.java         |   60 +
 .../waged/TestAssignmentMetadataStore.java         |  186 ++
 .../rebalancer/waged/TestWagedRebalancer.java      |  524 ++++++
 .../waged/TestWagedRebalancerMetrics.java          |  190 ++
 .../waged/constraints/MockRebalanceAlgorithm.java  |   84 +
 .../constraints/TestConstraintBasedAlgorithm.java  |   86 +
 .../constraints/TestFaultZoneAwareConstraint.java  |   79 +
 .../TestInstancePartitionsCountConstraint.java     |   63 +
 .../TestMaxCapacityUsageInstanceConstraint.java    |   57 +
 .../constraints/TestNodeCapacityConstraint.java    |   54 +
 .../TestNodeMaxPartitionLimitConstraint.java       |   56 +
 .../TestPartitionActivateConstraint.java           |   64 +
 .../TestPartitionMovementConstraint.java           |  127 ++
 ...estResourcePartitionAntiAffinityConstraint.java |   67 +
 ...TestResourceTopStateAntiAffinityConstraint.java |   82 +
 .../TestSamePartitionOnInstanceConstraint.java     |   59 +
 .../TestSoftConstraintNormalizeFunction.java       |   47 +
 .../constraints/TestValidGroupTagConstraint.java   |   66 +
 .../waged/model/AbstractTestClusterModel.java      |  203 +++
 .../waged/model/ClusterModelTestHelper.java        |   65 +
 .../rebalancer/waged/model/TestAssignableNode.java |  280 +++
 .../waged/model/TestAssignableReplica.java         |  167 ++
 .../rebalancer/waged/model/TestClusterContext.java |   93 +
 .../rebalancer/waged/model/TestClusterModel.java   |  101 ++
 .../waged/model/TestClusterModelProvider.java      |  376 ++++
 .../waged/model/TestOptimalAssignment.java         |   91 +
 .../helix/controller/stages/BaseStageTest.java     |    2 +-
 .../controller/stages/DummyClusterManager.java     |    2 +-
 .../TestBestPossibleCalcStageCompatibility.java    |    2 +-
 .../stages/TestCancellationMessageGeneration.java  |   97 ++
 .../helix/controller/stages/TestClusterEvent.java  |   44 +-
 .../stages/TestCompatibilityCheckStage.java        |    2 +-
 .../stages/TestCurrentStateComputationStage.java   |    2 +-
 .../stages/TestMessageThrottleStage.java           |    2 +-
 .../controller/stages/TestRebalancePipeline.java   |   71 +-
 .../stages/TestResourceComputationStage.java       |    2 +-
 .../helix/controller/stages/TestTaskStage.java     |    2 +-
 .../TestAddStateModelFactoryAfterConnect.java      |    2 +-
 .../helix/integration/TestBucketizedResource.java  |    2 +-
 .../integration/TestCarryOverBadCurState.java      |    2 +-
 .../helix/integration/TestCleanupExternalView.java |    2 +-
 .../TestCorrectnessOnConnectivityLoss.java         |    2 +-
 .../org/apache/helix/integration/TestDisable.java  |    2 +-
 .../integration/TestDisableCustomCodeRunner.java   |    2 +-
 .../helix/integration/TestDisableExternalView.java |    2 +-
 .../helix/integration/TestDisableResource.java     |    2 +-
 .../helix/integration/TestDistributedCMMain.java   |    2 +-
 .../TestDistributedClusterController.java          |    2 +-
 .../org/apache/helix/integration/TestDriver.java   |    6 +-
 .../helix/integration/TestEnableCompression.java   |   53 +-
 .../integration/TestEntropyFreeNodeBounce.java     |    4 +-
 .../helix/integration/TestExternalViewUpdates.java |    2 +-
 .../TestNoThrottleDisabledPartitions.java          |    2 +-
 .../apache/helix/integration/TestNullReplica.java  |    2 +-
 .../TestPartitionLevelTransitionConstraint.java    |    2 +-
 .../integration/TestPersistAssignmentStage.java    |    2 +-
 .../integration/TestPreferenceListAsQueue.java     |    4 +-
 .../helix/integration/TestRenamePartition.java     |    2 +-
 .../helix/integration/TestResetPartitionState.java |    2 +-
 .../integration/TestResourceGroupEndtoEnd.java     |    5 +-
 .../TestResourceWithSamePartitionKey.java          |    2 +-
 .../helix/integration/TestStandAloneCMMain.java    |   83 -
 .../integration/TestSyncSessionToController.java   |    2 +-
 .../integration/TestWeightBasedRebalanceUtil.java  |    2 +-
 .../integration/TestZkCallbackHandlerLeak.java     |    6 +-
 .../helix/integration/TestZkConnectionLost.java    |    6 +-
 .../TestControllerDataProviderSelectiveUpdate.java |    2 +-
 .../controller/TestControllerLeadershipChange.java |  165 +-
 .../controller/TestControllerLiveLock.java         |    2 +-
 .../controller/TestWatcherLeakageOnController.java |  102 ++
 .../manager/ClusterControllerManager.java          |   12 +-
 .../manager/ClusterDistributedController.java      |    5 +-
 .../manager/MockParticipantManager.java            |   15 +-
 .../manager/TestConsecutiveZkSessionExpiry.java    |    2 +-
 .../integration/manager/TestHelixDataAccessor.java |    2 +-
 .../manager/TestParticipantManager.java            |    2 +-
 .../helix/integration/manager/ZkTestManager.java   |    5 +-
 .../integration/messaging/TestBatchMessage.java    |    4 +-
 .../messaging/TestBatchMessageWrapper.java         |    2 +-
 .../integration/messaging/TestMessageThrottle.java |    4 +-
 .../messaging/TestMessageThrottle2.java            |    2 +-
 .../messaging/TestSchedulerMessage.java            |    2 +-
 .../messaging/TestSchedulerMessage2.java           |    2 +-
 .../messaging/TestSchedulerMsgContraints.java      |    2 +-
 .../messaging/TestSchedulerMsgUsingQueue.java      |    2 +-
 .../multizk/TestMultiZkHelixJavaApis.java          |  732 ++++++++
 .../paticipant/TestInstanceAutoJoin.java           |   62 +-
 .../TestStateTransitionTimeoutWithResource.java    |    2 +-
 .../TestCrushAutoRebalanceNonRack.java             |   22 +-
 .../rebalancer/CrushRebalancers/TestNodeSwap.java  |    4 +-
 .../TestDelayedAutoRebalance.java                  |   49 +-
 ...stDelayedAutoRebalanceWithDisabledInstance.java |   32 +-
 .../TestDelayedAutoRebalanceWithRackaware.java     |    2 +-
 .../PartitionMigration/TestExpandCluster.java      |    4 +-
 .../TestPartitionMigrationBase.java                |   26 +-
 .../TestWagedRebalancerMigration.java              |  111 ++
 .../rebalancer/TestAutoIsWithEmptyMap.java         |    2 +-
 .../integration/rebalancer/TestAutoRebalance.java  |    6 +-
 .../TestAutoRebalancePartitionLimit.java           |    6 +-
 .../TestCustomizedIdealStateRebalancer.java        |    6 +-
 .../rebalancer/TestFullAutoNodeTagging.java        |    8 +-
 .../rebalancer/TestMixedModeAutoRebalance.java     |  181 +-
 .../rebalancer/TestZeroReplicaAvoidance.java       |   62 +-
 .../WagedRebalancer/TestDelayedWagedRebalance.java |   89 +
 ...tDelayedWagedRebalanceWithDisabledInstance.java |   96 +
 .../TestDelayedWagedRebalanceWithRackaware.java    |   96 +
 .../TestMixedModeWagedRebalance.java               |   58 +
 .../TestWagedExpandCluster.java}                   |   47 +-
 .../WagedRebalancer/TestWagedNodeSwap.java         |  294 ++++
 .../WagedRebalancer/TestWagedRebalance.java        |  593 +++++++
 .../TestWagedRebalanceFaultZone.java               |  372 ++++
 .../TestWagedRebalanceTopologyAware.java           |  114 ++
 .../TestRoutingTableProviderFromCurrentStates.java |  174 +-
 .../helix/integration/task/TaskTestUtil.java       |    2 +-
 .../task/TestTaskSchedulingTwoCurrentStates.java   |  216 +++
 .../helix/integration/task/TestTaskStopQueue.java  |    2 +-
 .../task/TestWorkflowContextWithoutConfig.java     |   73 +-
 .../helix/integration/task/WorkflowGenerator.java  |   27 +-
 .../manager/zk/TestAddBuiltInStateModelDef.java    |    2 +-
 .../helix/manager/zk/TestHandleNewSession.java     |  197 ---
 .../apache/helix/manager/zk/TestHandleSession.java |  628 +++++++
 .../helix/manager/zk/TestParticipantManager.java   |  184 ++
 .../apache/helix/manager/zk/TestRawZkClient.java   |  478 ++++-
 .../manager/zk/TestWtCacheAsyncOpMultiThread.java  |    6 +-
 .../manager/zk/TestWtCacheAsyncOpSingleThread.java |    6 +-
 .../manager/zk/TestWtCacheSyncOpSingleThread.java  |    4 +-
 .../org/apache/helix/manager/zk/TestZKUtil.java    |   30 +-
 .../org/apache/helix/manager/zk/TestZKWatch.java   |  153 ++
 .../helix/manager/zk/TestZNRecordSerializer.java   |    4 +-
 .../helix/manager/zk/TestZNRecordSizeLimit.java    |  326 +++-
 .../zk/TestZNRecordStreamingSerializer.java        |    2 +-
 .../helix/manager/zk/TestZkBaseDataAccessor.java   |   30 +-
 .../helix/manager/zk/TestZkBucketDataAccessor.java |  189 ++
 .../manager/zk/TestZkCacheAsyncOpSingleThread.java |   98 +-
 .../manager/zk/TestZkCacheSyncOpSingleThread.java  |    8 +-
 .../helix/manager/zk/TestZkClusterManager.java     |    2 +-
 .../apache/helix/manager/zk/TestZkFlapping.java    |    2 +
 .../apache/helix/manager/zk/TestZkHelixAdmin.java  |  215 ++-
 .../apache/helix/manager/zk/TestZkReconnect.java   |    4 +-
 .../helix/manager/zk/client/TestHelixZkClient.java |   29 +-
 .../serializer/TestJacksonPayloadSerializer.java   |    2 +-
 .../messaging/TestDefaultMessagingService.java     |    2 +-
 .../apache/helix/mock/MockBaseDataAccessor.java    |    8 +-
 .../java/org/apache/helix/mock/MockHelixAdmin.java |   35 +-
 .../java/org/apache/helix/mock/MockManager.java    |    2 +-
 .../java/org/apache/helix/mock/MockZkClient.java   |    4 +-
 .../apache/helix/mock/MockZkHelixDataAccessor.java |    2 +-
 .../helix/mock/controller/MockController.java      |    6 +-
 .../participant/StoreAccessDiffNodeTransition.java |    7 +-
 .../participant/StoreAccessOneNodeTransition.java  |    7 +-
 .../helix/mock/spectator/MockSpectatorProcess.java |    9 +-
 .../org/apache/helix/model/TestClusterConfig.java  |  259 +++
 .../org/apache/helix/model/TestConstraint.java     |    2 +-
 .../org/apache/helix/model/TestInstanceConfig.java |   73 +-
 .../org/apache/helix/model/TestResourceConfig.java |  271 +++
 .../apache/helix/model/TestStateModelValidity.java |    2 +-
 .../helix/model/TestStateTransitionProperty.java   |    2 +-
 .../apache/helix/model/cloud/TestCloudConfig.java  |  224 +++
 .../helix/monitoring/TestZKPathDataDumpTask.java   |    2 +-
 .../mbeans/TestClusterStatusMonitor.java           |  194 ++-
 .../monitoring/mbeans/TestInstanceMonitor.java     |   75 +
 .../monitoring/mbeans/TestResourceMonitor.java     |  373 ++--
 .../mbeans/TestRoutingTableProviderMonitor.java    |   10 +-
 .../monitoring/mbeans/TestZkClientMonitor.java     |    9 +-
 .../helix/participant/MockZKHelixManager.java      |    4 +-
 .../participant/TestDistControllerStateModel.java  |    2 +-
 .../TestDistControllerStateModelFactory.java       |    2 +-
 .../helix/spectator/TestRoutingDataCache.java      |    2 +-
 .../org/apache/helix/store/TestJsonComparator.java |    2 +-
 .../store/zk/TestAutoFallbackPropertyStore.java    |    4 +-
 .../helix/store/zk/TestZkHelixPropertyStore.java   |    8 +-
 .../zk/TestZkManagerWithAutoFallbackStore.java     |    2 +-
 .../helix/task/TestAssignableInstanceManager.java  |    2 +-
 .../helix/task/TestTargetedTaskStateChange.java    |  329 ++++
 .../org/apache/helix/tools/TestClusterSetup.java   |  102 +-
 .../apache/helix/tools/TestClusterVerifier.java    |   76 +-
 .../org/apache/helix/tools/TestHelixAdminCli.java  |    2 +-
 .../java/org/apache/helix/tools/TestZkCopy.java    |    2 +-
 .../apache/helix/util/TestPropertyKeyGetPath.java  |    2 +-
 .../org/apache/helix/util/TestZKClientPool.java    |    8 +-
 helix-core/src/test/resources/AzureResponse.json   |  104 ++
 ...eUsageCalculator.MeasureBaselineDivergence.json |   37 +
 ...chExternalViewVerifier.ComputeIdealMapping.json |   14 +-
 helix-rest/pom.xml                                 |   14 +-
 .../rest/common/HelixDataAccessorWrapper.java      |    2 +-
 .../helix/rest/common/HelixRestNamespace.java      |   38 +-
 .../apache/helix/rest/common/HttpConstants.java    |   15 +-
 .../org/apache/helix/rest/common/ServletType.java  |   15 +-
 .../rest/metadatastore/MetadataStoreDirectory.java |  137 ++
 .../metadatastore/ZkMetadataStoreDirectory.java    |  374 ++++
 .../accessor/MetadataStoreRoutingDataReader.java   |   50 +
 .../accessor/MetadataStoreRoutingDataWriter.java   |   74 +
 .../accessor/ZkRoutingDataReader.java              |  160 ++
 .../accessor/ZkRoutingDataWriter.java              |  428 +++++
 .../concurrency/ZkDistributedLeaderElection.java   |  141 ++
 .../datamodel/MetadataStoreShardingKey.java        |   61 +
 .../MetadataStoreShardingKeysByRealm.java          |   56 +
 .../apache/helix/rest/server/HelixRestMain.java    |    8 +-
 .../apache/helix/rest/server/HelixRestServer.java  |    5 +-
 .../apache/helix/rest/server/ServerContext.java    |  302 +++-
 .../rest/server/json/instance/InstanceInfo.java    |    2 +-
 .../rest/server/resources/AbstractResource.java    |   10 +-
 .../resources/helix/AbstractHelixResource.java     |   16 +-
 .../server/resources/helix/ClusterAccessor.java    |  494 ++++--
 .../server/resources/helix/InstancesAccessor.java  |   88 +-
 .../rest/server/resources/helix/JobAccessor.java   |    4 +-
 .../resources/helix/PerInstanceAccessor.java       |   58 +-
 .../resources/helix/PropertyStoreAccessor.java     |   19 +-
 .../server/resources/helix/ResourceAccessor.java   |  160 +-
 .../rest/server/resources/helix/TaskAccessor.java  |    2 +-
 .../server/resources/helix/WorkflowAccessor.java   |    4 +-
 .../MetadataStoreDirectoryAccessor.java            |  392 +++++
 .../resources/zookeeper/ZooKeeperAccessor.java     |  174 ++
 .../rest/server/service/InstanceServiceImpl.java   |    8 +
 .../rest/common/TestHelixDataAccessorWrapper.java  |    2 +-
 .../TestZkMetadataStoreDirectory.java              |  375 ++++
 .../accessor/TestZkRoutingDataReader.java          |  136 ++
 .../accessor/TestZkRoutingDataWriter.java          |  223 +++
 .../integration/TestRoutingDataUpdate.java         |  176 ++
 .../helix/rest/server/AbstractTestClass.java       |  192 +-
 .../MetadataStoreDirectoryAccessorTestBase.java    |  149 ++
 .../helix/rest/server/TestClusterAccessor.java     |  443 ++++-
 .../helix/rest/server/TestInstancesAccessor.java   |   77 +
 .../rest/server/TestMSDAccessorLeaderElection.java |  261 +++
 .../server/TestMetadataStoreDirectoryAccessor.java |  516 ++++++
 .../helix/rest/server/TestPerInstanceAccessor.java |   68 +-
 .../rest/server/TestPropertyStoreAccessor.java     |    8 +-
 .../helix/rest/server/TestResourceAccessor.java    |  131 +-
 .../helix/rest/server/TestZooKeeperAccessor.java   |  160 ++
 .../mock/MockMetadataStoreDirectoryAccessor.java   |  124 ++
 .../rest/server/util/JerseyUriRequestBuilder.java  |    3 +-
 helix-style-intellij.xml                           |    4 +-
 .../LICENSE                                        |    0
 .../NOTICE                                         |    0
 ...adata-store-directory-common-0.9.2-SNAPSHOT.ivy |   42 +-
 .../pom.xml                                        |   95 +-
 .../src/assemble/assembly.xml                      |   60 +
 .../src/main/config/log4j.properties               |    0
 .../msdcommon/callback/RoutingDataListener.java    |   11 +-
 .../constant/MetadataStoreRoutingConstants.java    |   95 +
 .../datamodel/MetadataStoreRoutingData.java        |   78 +
 .../helix/msdcommon/datamodel/TrieRoutingData.java |  327 ++++
 .../exception/InvalidRoutingDataException.java     |   18 +-
 .../mock/MockMetadataStoreDirectoryServer.java     |  161 ++
 .../helix/msdcommon/util/ZkValidationUtil.java     |   33 +-
 .../src/test/conf/testng.xml                       |   27 +
 .../helix/msdcommon/constant/TestConstants.java    |   23 +-
 .../msdcommon/datamodel/TestTrieRoutingData.java   |  346 ++++
 .../mock/TestMockMetadataStoreDirectoryServer.java |  115 ++
 .../src/test/resources/log4j.properties            |    0
 metrics-common/LICENSE                             |  270 +++
 metrics-common/NOTICE                              |   37 +
 .../metrics-common-0.9.2-SNAPSHOT.ivy              |   35 +-
 {helix-admin-webapp => metrics-common}/pom.xml     |   74 +-
 metrics-common/src/assemble/assembly.xml           |   60 +
 .../helix/monitoring/SensorNameProvider.java       |    0
 .../helix/monitoring/mbeans/MBeanRegistrar.java    |    1 +
 .../monitoring/mbeans/MonitorDomainNames.java      |    0
 .../mbeans/dynamicMBeans/DynamicMBeanProvider.java |   41 +-
 .../mbeans/dynamicMBeans/DynamicMetric.java        |    5 +-
 .../dynamicMBeans/HistogramDynamicMetric.java      |    1 +
 .../mbeans/dynamicMBeans/SimpleDynamicMetric.java  |    0
 .../mbeans/exception/MetricException.java          |   18 +-
 metrics-common/src/test/conf/testng.xml            |   27 +
 .../src/test/resources/log4j.properties            |    0
 pom.xml                                            |   31 +
 .../apache/helix/lockmanager/LockManagerDemo.java  |    7 +-
 .../apache/helix/recipes/rabbitmq/Consumer.java    |    2 +-
 .../recipes/rabbitmq/SetupConsumerCluster.java     |    2 +-
 recipes/rsync-replicated-file-system/pom.xml       |    5 -
 .../java/org/apache/helix/filestore/FileStore.java |    2 +-
 .../helix/filestore/FileStoreStateModel.java       |    4 +-
 .../apache/helix/filestore/IntegrationTest.java    |    8 +-
 .../org/apache/helix/filestore/Replicator.java     |    2 +-
 .../org/apache/helix/filestore/SetupCluster.java   |    2 +-
 .../helix/servicediscovery/ServiceDiscovery.java   |    2 +-
 .../apache/helix/taskexecution/TaskCluster.java    |    2 +-
 .../helix/taskexecution/TaskExecutionDemo.java     |    7 +-
 .../org/apache/helix/taskexecution/Worker.java     |    2 +-
 zookeeper-api/LICENSE                              |  270 +++
 zookeeper-api/NOTICE                               |   37 +
 {helix-admin-webapp => zookeeper-api}/pom.xml      |   91 +-
 zookeeper-api/src/assemble/assembly.xml            |   60 +
 .../src/main/config/log4j.properties               |    0
 .../helix/zookeeper/api/client/HelixZkClient.java  |  157 ++
 .../zookeeper/api/client/RealmAwareZkClient.java   |  606 +++++++
 .../helix/zookeeper/api/client/ZkClientType.java   |   32 +-
 .../api/factory/RealmAwareZkClientFactory.java     |   56 +
 .../zookeeper/constant/ZkSystemPropertyKeys.java   |   53 +
 .../helix/zookeeper/datamodel}/ZNRecord.java       |   56 +-
 .../zookeeper/datamodel}/ZNRecordAssembler.java    |    3 +-
 .../zookeeper/datamodel}/ZNRecordBucketizer.java   |    3 +-
 .../helix/zookeeper/datamodel}/ZNRecordDelta.java  |    2 +-
 .../zookeeper/datamodel}/ZNRecordUpdater.java      |    5 +-
 .../serializer/JacksonPayloadSerializer.java       |    8 +-
 .../datamodel}/serializer/PayloadSerializer.java   |    2 +-
 .../serializer/ZNRecordJacksonSerializer.java      |   68 +
 .../datamodel/serializer}/ZNRecordSerializer.java  |   52 +-
 .../serializer}/ZNRecordStreamingSerializer.java   |   50 +-
 .../zookeeper/exception/ZkClientException.java     |   14 +-
 .../zookeeper/impl/client/DedicatedZkClient.java   |  504 ++++++
 .../zookeeper/impl/client/FederatedZkClient.java   |  574 ++++++
 .../zookeeper/impl/client/SharedZkClient.java      |  534 ++++++
 .../helix/zookeeper/impl/client}/ZkClient.java     |   59 +-
 .../impl/factory/DedicatedZkClientFactory.java     |   69 +
 .../impl/factory/HelixZkClientFactory.java         |   71 +
 .../impl/factory/SharedZkClientFactory.java        |  209 +++
 .../impl/factory}/ZkConnectionManager.java         |   55 +-
 .../helix/zookeeper}/util/GZipCompressionUtil.java |    5 +-
 .../zookeeper/util/HttpRoutingDataReader.java      |  197 +++
 .../apache/helix/zookeeper/util/ZNRecordUtil.java  |   65 +
 .../helix/zookeeper/zkclient/DataUpdater.java      |   34 +
 .../helix/zookeeper/zkclient/ExceptionUtil.java    |   52 +
 .../zookeeper/zkclient/IDefaultNameSpace.java      |   27 +
 .../helix/zookeeper/zkclient/IZkChildListener.java |   41 +
 .../helix/zookeeper/zkclient/IZkConnection.java    |   60 +
 .../helix/zookeeper/zkclient/IZkDataListener.java  |   30 +
 .../zookeeper/zkclient}/IZkStateListener.java      |    2 +-
 .../helix/zookeeper/zkclient/NetworkUtil.java      |  122 ++
 .../apache/helix/zookeeper/zkclient}/ZkClient.java |  589 +++++--
 .../helix/zookeeper/zkclient}/ZkConnection.java    |    6 +-
 .../helix/zookeeper/zkclient}/ZkEventThread.java   |    5 +-
 .../apache/helix/zookeeper/zkclient/ZkLock.java    |   56 +
 .../apache/helix/zookeeper/zkclient/ZkServer.java  |  175 ++
 .../zookeeper/zkclient/annotation}/PreFetch.java   |    2 +-
 .../zkclient/callback}/ZkAsyncCallbacks.java       |   11 +-
 .../zkclient/deprecated/IZkStateListener.java      |   53 +
 .../zkclient/exception/ZkBadVersionException.java  |   39 +
 .../zookeeper/zkclient/exception/ZkException.java  |   71 +
 .../zkclient/exception/ZkInterruptedException.java |   26 +
 .../zkclient/exception/ZkMarshallingError.java     |   37 +
 .../zkclient/exception/ZkNoNodeException.java      |   39 +
 .../zkclient/exception/ZkNodeExistsException.java  |   39 +
 .../exception/ZkSessionMismatchedException.java    |   22 +-
 .../zkclient/exception/ZkTimeoutException.java     |   37 +
 .../zkclient/metric}/ZkClientMonitor.java          |   15 +-
 .../zkclient/metric}/ZkClientPathMonitor.java      |    4 +-
 .../zkclient/serialize}/BasicZkSerializer.java     |    4 +-
 .../serialize/BytesPushThroughSerializer.java      |   36 +
 .../zkclient/serialize}/PathBasedZkSerializer.java |    5 +-
 .../zkclient/serialize/SerializableSerializer.java |   55 +
 .../serialize/TcclAwareObjectIputStream.java       |   82 +
 .../zookeeper/zkclient/serialize/ZkSerializer.java |   33 +
 .../zkclient}/util/ExponentialBackoffStrategy.java |   22 +-
 zookeeper-api/src/test/conf/testng.xml             |   27 +
 .../helix/zookeeper/constant/TestConstants.java    |   45 +
 .../TestZNRecordSerializeWriteSizeLimit.java       |  205 +++
 .../apache/helix/zookeeper/impl/ZkTestBase.java    |  148 ++
 .../client/RealmAwareZkClientFactoryTestBase.java  |  180 ++
 .../impl/client/RealmAwareZkClientTestBase.java    |   66 +
 .../impl/client/TestDedicatedZkClient.java         |   27 +-
 .../impl/client/TestFederatedZkClient.java         |  283 +++
 .../zookeeper/impl/client/TestSharedZkClient.java  |   69 +
 .../zookeeper/util/TestHttpRoutingDataReader.java  |  120 ++
 .../src/test/resources/log4j.properties            |    0
 .../zookeeper-api-0.9.2-SNAPSHOT.ivy               |   39 +-
 681 files changed, 38376 insertions(+), 8020 deletions(-)
 create mode 100644 helix-common/LICENSE
 create mode 100644 helix-common/NOTICE
 copy helix-core/helix-core-0.9.2-SNAPSHOT.ivy => helix-common/helix-common-0.9.2-SNAPSHOT.ivy (56%)
 copy {helix-admin-webapp => helix-common}/pom.xml (58%)
 create mode 100644 helix-common/src/assemble/assembly.xml
 copy {helix-core => helix-common}/src/main/java/org/apache/helix/HelixException.java (100%)
 create mode 100644 helix-common/src/main/java/org/apache/helix/SystemPropertyKeys.java
 copy helix-core/src/main/java/org/apache/helix/HelixException.java => helix-common/src/main/java/org/apache/helix/ZNRecord.java (52%)
 copy helix-core/src/main/java/org/apache/helix/HelixTimerTask.java => helix-common/src/main/java/org/apache/helix/ZNRecordDelta.java (78%)
 copy helix-core/src/main/java/org/apache/helix/monitoring/mbeans/MonitorDomainNames.java => helix-common/src/main/java/org/apache/helix/manager/zk/serializer/JacksonPayloadSerializer.java (71%)
 copy helix-core/src/main/java/org/apache/helix/monitoring/mbeans/MonitorDomainNames.java => helix-common/src/main/java/org/apache/helix/manager/zk/serializer/PayloadSerializer.java (71%)
 create mode 100644 helix-common/src/test/conf/testng.xml
 copy {helix-rest => helix-common}/src/test/resources/log4j.properties (100%)
 copy helix-core/src/main/java/org/apache/helix/{manager/zk/PathBasedZkSerializer.java => BucketDataAccessor.java} (50%)
 create mode 100644 helix-core/src/main/java/org/apache/helix/HelixCloudProperty.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/HelixManagerProperty.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/HelixPropertyFactory.java
 copy helix-core/src/main/java/org/apache/helix/{HelixException.java => HelixRebalanceException.java} (50%)
 delete mode 100644 helix-core/src/main/java/org/apache/helix/SystemPropertyKeys.java
 copy helix-core/src/main/java/org/apache/helix/{HelixTimerTask.java => api/cloud/CloudInstanceInformation.java} (62%)
 copy helix-core/src/main/java/org/apache/helix/{task/beans/WorkflowBean.java => api/cloud/CloudInstanceInformationProcessor.java} (56%)
 create mode 100644 helix-core/src/main/java/org/apache/helix/cloud/azure/AzureCloudInstanceInformation.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/cloud/azure/AzureCloudInstanceInformationProcessor.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/cloud/azure/AzureConstants.java
 copy helix-core/src/main/java/org/apache/helix/{monitoring/SensorNameProvider.java => cloud/constants/CloudProvider.java} (88%)
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/changedetector/ChangeDetector.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeDetector.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeSnapshot.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/StatefulRebalancer.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/DelayedRebalanceUtil.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/WagedValidationUtil.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/AssignmentMetadataStore.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/RebalanceAlgorithm.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/WagedRebalancer.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ConstraintBasedAlgorithm.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ConstraintBasedAlgorithmFactory.java
 copy helix-core/src/main/java/org/apache/helix/{manager/zk/ByteArraySerializer.java => controller/rebalancer/waged/constraints/FaultZoneAwareConstraint.java} (51%)
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/HardConstraint.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/InstancePartitionsCountConstraint.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/MaxCapacityUsageInstanceConstraint.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/NodeCapacityConstraint.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/NodeMaxPartitionLimitConstraint.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/PartitionMovementConstraint.java
 copy helix-core/src/main/java/org/apache/helix/{manager/zk/ByteArraySerializer.java => controller/rebalancer/waged/constraints/ReplicaActivateConstraint.java} (52%)
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ResourcePartitionAntiAffinityConstraint.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ResourceTopStateAntiAffinityConstraint.java
 copy helix-core/src/main/java/org/apache/helix/{store/ZNRecordJsonSerializer.java => controller/rebalancer/waged/constraints/SamePartitionOnInstanceConstraint.java} (54%)
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/SoftConstraint.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/UsageSoftConstraint.java
 copy helix-core/src/main/java/org/apache/helix/{store/ZNRecordJsonSerializer.java => controller/rebalancer/waged/constraints/ValidGroupTagConstraint.java} (54%)
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/AssignableNode.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/AssignableReplica.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterContext.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterModel.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterModelProvider.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/OptimalAssignment.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/manager/zk/GenericBaseDataAccessorBuilder.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/manager/zk/GenericZkHelixApiBuilder.java
 copy helix-core/src/main/java/org/apache/helix/manager/zk/{ByteArraySerializer.java => ZNRecordJacksonSerializer.java} (68%)
 create mode 100644 helix-core/src/main/java/org/apache/helix/manager/zk/ZkBucketDataAccessor.java
 delete mode 100644 helix-core/src/main/java/org/apache/helix/manager/zk/client/HelixZkClientFactory.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/model/CloudConfig.java
 delete mode 100644 helix-core/src/main/java/org/apache/helix/monitoring/mbeans/InstanceMonitorMBean.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/monitoring/metrics/MetricCollector.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/monitoring/metrics/WagedRebalancerMetricCollector.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/BaselineDivergenceGauge.java
 copy helix-core/src/main/java/org/apache/helix/{HelixTimerTask.java => monitoring/metrics/implementation/RebalanceCounter.java} (61%)
 copy helix-core/src/main/java/org/apache/helix/{HelixTimerTask.java => monitoring/metrics/implementation/RebalanceFailureCount.java} (69%)
 create mode 100644 helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/RebalanceLatencyGauge.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/CountMetric.java
 create mode 100644 helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/LatencyMetric.java
 copy helix-core/src/main/java/org/apache/helix/{HelixTimerTask.java => monitoring/metrics/model/Metric.java} (57%)
 copy helix-core/src/main/java/org/apache/helix/monitoring/{mbeans/dynamicMBeans/SimpleDynamicMetric.java => metrics/model/RatioMetric.java} (52%)
 create mode 100644 helix-core/src/main/resources/azure-cloud.properties
 create mode 100644 helix-core/src/main/resources/helix-manager.properties
 create mode 100644 helix-core/src/main/resources/soft-constraint-weight.properties
 create mode 100644 helix-core/src/test/java/org/apache/helix/cloud/MockHttpClient.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/cloud/TestAzureCloudInstanceInformationProcessor.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/changedetector/TestResourceChangeDetector.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/dataproviders/TestResourceControllerDataProvider.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/util/TestResourceUsageCalculator.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/MockAssignmentMetadataStore.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/TestAssignmentMetadataStore.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/TestWagedRebalancer.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/TestWagedRebalancerMetrics.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/MockRebalanceAlgorithm.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestConstraintBasedAlgorithm.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestFaultZoneAwareConstraint.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestInstancePartitionsCountConstraint.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestMaxCapacityUsageInstanceConstraint.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestNodeCapacityConstraint.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestNodeMaxPartitionLimitConstraint.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestPartitionActivateConstraint.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestPartitionMovementConstraint.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestResourcePartitionAntiAffinityConstraint.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestResourceTopStateAntiAffinityConstraint.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestSamePartitionOnInstanceConstraint.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestSoftConstraintNormalizeFunction.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/constraints/TestValidGroupTagConstraint.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/model/AbstractTestClusterModel.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/model/ClusterModelTestHelper.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/model/TestAssignableNode.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/model/TestAssignableReplica.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/model/TestClusterContext.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/model/TestClusterModel.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/model/TestClusterModelProvider.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/model/TestOptimalAssignment.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/controller/stages/TestCancellationMessageGeneration.java
 delete mode 100644 helix-core/src/test/java/org/apache/helix/integration/TestStandAloneCMMain.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/controller/TestWatcherLeakageOnController.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/multizk/TestMultiZkHelixJavaApis.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/rebalancer/PartitionMigration/TestWagedRebalancerMigration.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/rebalancer/WagedRebalancer/TestDelayedWagedRebalance.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/rebalancer/WagedRebalancer/TestDelayedWagedRebalanceWithDisabledInstance.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/rebalancer/WagedRebalancer/TestDelayedWagedRebalanceWithRackaware.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/rebalancer/WagedRebalancer/TestMixedModeWagedRebalance.java
 copy helix-core/src/test/java/org/apache/helix/integration/rebalancer/{PartitionMigration/TestExpandCluster.java => WagedRebalancer/TestWagedExpandCluster.java} (74%)
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/rebalancer/WagedRebalancer/TestWagedNodeSwap.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/rebalancer/WagedRebalancer/TestWagedRebalance.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/rebalancer/WagedRebalancer/TestWagedRebalanceFaultZone.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/rebalancer/WagedRebalancer/TestWagedRebalanceTopologyAware.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/integration/task/TestTaskSchedulingTwoCurrentStates.java
 delete mode 100644 helix-core/src/test/java/org/apache/helix/manager/zk/TestHandleNewSession.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/manager/zk/TestHandleSession.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/manager/zk/TestParticipantManager.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/manager/zk/TestZKWatch.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/manager/zk/TestZkBucketDataAccessor.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/model/TestClusterConfig.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/model/TestResourceConfig.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/model/cloud/TestCloudConfig.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/monitoring/mbeans/TestInstanceMonitor.java
 create mode 100644 helix-core/src/test/java/org/apache/helix/task/TestTargetedTaskStateChange.java
 create mode 100644 helix-core/src/test/resources/AzureResponse.json
 create mode 100644 helix-core/src/test/resources/TestResourceUsageCalculator.MeasureBaselineDivergence.json
 copy helix-core/src/main/java/org/apache/helix/monitoring/SensorNameProvider.java => helix-rest/src/main/java/org/apache/helix/rest/common/HttpConstants.java (75%)
 create mode 100644 helix-rest/src/main/java/org/apache/helix/rest/metadatastore/MetadataStoreDirectory.java
 create mode 100644 helix-rest/src/main/java/org/apache/helix/rest/metadatastore/ZkMetadataStoreDirectory.java
 create mode 100644 helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/MetadataStoreRoutingDataReader.java
 create mode 100644 helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/MetadataStoreRoutingDataWriter.java
 create mode 100644 helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/ZkRoutingDataReader.java
 create mode 100644 helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/ZkRoutingDataWriter.java
 create mode 100644 helix-rest/src/main/java/org/apache/helix/rest/metadatastore/concurrency/ZkDistributedLeaderElection.java
 create mode 100644 helix-rest/src/main/java/org/apache/helix/rest/metadatastore/datamodel/MetadataStoreShardingKey.java
 create mode 100644 helix-rest/src/main/java/org/apache/helix/rest/metadatastore/datamodel/MetadataStoreShardingKeysByRealm.java
 create mode 100644 helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadatastore/MetadataStoreDirectoryAccessor.java
 create mode 100644 helix-rest/src/main/java/org/apache/helix/rest/server/resources/zookeeper/ZooKeeperAccessor.java
 create mode 100644 helix-rest/src/test/java/org/apache/helix/rest/metadatastore/TestZkMetadataStoreDirectory.java
 create mode 100644 helix-rest/src/test/java/org/apache/helix/rest/metadatastore/accessor/TestZkRoutingDataReader.java
 create mode 100644 helix-rest/src/test/java/org/apache/helix/rest/metadatastore/accessor/TestZkRoutingDataWriter.java
 create mode 100644 helix-rest/src/test/java/org/apache/helix/rest/metadatastore/integration/TestRoutingDataUpdate.java
 create mode 100644 helix-rest/src/test/java/org/apache/helix/rest/server/MetadataStoreDirectoryAccessorTestBase.java
 create mode 100644 helix-rest/src/test/java/org/apache/helix/rest/server/TestMSDAccessorLeaderElection.java
 create mode 100644 helix-rest/src/test/java/org/apache/helix/rest/server/TestMetadataStoreDirectoryAccessor.java
 create mode 100644 helix-rest/src/test/java/org/apache/helix/rest/server/TestZooKeeperAccessor.java
 create mode 100644 helix-rest/src/test/java/org/apache/helix/rest/server/mock/MockMetadataStoreDirectoryAccessor.java
 copy {helix-rest => metadata-store-directory-common}/LICENSE (100%)
 copy {helix-rest => metadata-store-directory-common}/NOTICE (100%)
 copy helix-core/helix-core-0.9.2-SNAPSHOT.ivy => metadata-store-directory-common/metadata-store-directory-common-0.9.2-SNAPSHOT.ivy (55%)
 copy {helix-admin-webapp => metadata-store-directory-common}/pom.xml (55%)
 create mode 100644 metadata-store-directory-common/src/assemble/assembly.xml
 copy {helix-rest => metadata-store-directory-common}/src/main/config/log4j.properties (100%)
 copy helix-core/src/main/java/org/apache/helix/monitoring/SensorNameProvider.java => metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/callback/RoutingDataListener.java (75%)
 create mode 100644 metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/constant/MetadataStoreRoutingConstants.java
 create mode 100644 metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/datamodel/MetadataStoreRoutingData.java
 create mode 100644 metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/datamodel/TrieRoutingData.java
 copy helix-core/src/main/java/org/apache/helix/monitoring/mbeans/MonitorDomainNames.java => metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/exception/InvalidRoutingDataException.java (67%)
 create mode 100644 metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/mock/MockMetadataStoreDirectoryServer.java
 copy helix-core/src/main/java/org/apache/helix/HelixTimerTask.java => metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/util/ZkValidationUtil.java (60%)
 create mode 100644 metadata-store-directory-common/src/test/conf/testng.xml
 copy helix-core/src/main/java/org/apache/helix/healthcheck/ParticipantHealthReportCollector.java => metadata-store-directory-common/src/test/java/org/apache/helix/msdcommon/constant/TestConstants.java (59%)
 create mode 100644 metadata-store-directory-common/src/test/java/org/apache/helix/msdcommon/datamodel/TestTrieRoutingData.java
 create mode 100644 metadata-store-directory-common/src/test/java/org/apache/helix/msdcommon/mock/TestMockMetadataStoreDirectoryServer.java
 copy {helix-rest => metadata-store-directory-common}/src/test/resources/log4j.properties (100%)
 create mode 100644 metrics-common/LICENSE
 create mode 100644 metrics-common/NOTICE
 copy helix-core/helix-core-0.9.2-SNAPSHOT.ivy => metrics-common/metrics-common-0.9.2-SNAPSHOT.ivy (53%)
 copy {helix-admin-webapp => metrics-common}/pom.xml (55%)
 create mode 100644 metrics-common/src/assemble/assembly.xml
 rename {helix-core => metrics-common}/src/main/java/org/apache/helix/monitoring/SensorNameProvider.java (100%)
 rename {helix-core => metrics-common}/src/main/java/org/apache/helix/monitoring/mbeans/MBeanRegistrar.java (99%)
 copy {helix-core => metrics-common}/src/main/java/org/apache/helix/monitoring/mbeans/MonitorDomainNames.java (100%)
 copy {helix-core => metrics-common}/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMBeanProvider.java (87%)
 rename {helix-core => metrics-common}/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMetric.java (95%)
 rename {helix-core => metrics-common}/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/HistogramDynamicMetric.java (99%)
 copy {helix-core => metrics-common}/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/SimpleDynamicMetric.java (100%)
 copy helix-core/src/main/java/org/apache/helix/api/listeners/PreFetch.java => metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/exception/MetricException.java (70%)
 create mode 100644 metrics-common/src/test/conf/testng.xml
 copy {helix-rest => metrics-common}/src/test/resources/log4j.properties (100%)
 create mode 100644 zookeeper-api/LICENSE
 create mode 100644 zookeeper-api/NOTICE
 copy {helix-admin-webapp => zookeeper-api}/pom.xml (64%)
 create mode 100644 zookeeper-api/src/assemble/assembly.xml
 copy {helix-rest => zookeeper-api}/src/main/config/log4j.properties (100%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/HelixZkClient.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/RealmAwareZkClient.java
 copy helix-core/src/main/java/org/apache/helix/manager/zk/PathBasedZkSerializer.java => zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/ZkClientType.java (54%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/factory/RealmAwareZkClientFactory.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/constant/ZkSystemPropertyKeys.java
 rename {helix-core/src/main/java/org/apache/helix => zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel}/ZNRecord.java (91%)
 copy {helix-core/src/main/java/org/apache/helix => zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel}/ZNRecordAssembler.java (96%)
 copy {helix-core/src/main/java/org/apache/helix => zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel}/ZNRecordBucketizer.java (98%)
 rename {helix-core/src/main/java/org/apache/helix => zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel}/ZNRecordDelta.java (97%)
 copy {helix-core/src/main/java/org/apache/helix => zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel}/ZNRecordUpdater.java (92%)
 rename {helix-core/src/main/java/org/apache/helix/manager/zk => zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel}/serializer/JacksonPayloadSerializer.java (94%)
 rename {helix-core/src/main/java/org/apache/helix/manager/zk => zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel}/serializer/PayloadSerializer.java (96%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/ZNRecordJacksonSerializer.java
 copy {helix-core/src/main/java/org/apache/helix/manager/zk => zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer}/ZNRecordSerializer.java (69%)
 copy {helix-core/src/main/java/org/apache/helix/manager/zk => zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer}/ZNRecordStreamingSerializer.java (86%)
 copy helix-core/src/main/java/org/apache/helix/HelixException.java => zookeeper-api/src/main/java/org/apache/helix/zookeeper/exception/ZkClientException.java (69%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/DedicatedZkClient.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java
 copy {helix-core/src/main/java/org/apache/helix/manager/zk => zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client}/ZkClient.java (83%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/DedicatedZkClientFactory.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/HelixZkClientFactory.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/SharedZkClientFactory.java
 copy {helix-core/src/main/java/org/apache/helix/manager/zk/client => zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory}/ZkConnectionManager.java (62%)
 copy {helix-core/src/main/java/org/apache/helix => zookeeper-api/src/main/java/org/apache/helix/zookeeper}/util/GZipCompressionUtil.java (97%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/util/HttpRoutingDataReader.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/util/ZNRecordUtil.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/DataUpdater.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ExceptionUtil.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IDefaultNameSpace.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkChildListener.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkConnection.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkDataListener.java
 copy {helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient}/IZkStateListener.java (98%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/NetworkUtil.java
 copy {helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient}/ZkClient.java (70%)
 copy {helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient}/ZkConnection.java (97%)
 rename {helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient}/ZkEventThread.java (97%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkLock.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkServer.java
 copy {helix-core/src/main/java/org/apache/helix/api/listeners => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/annotation}/PreFetch.java (94%)
 copy {helix-core/src/main/java/org/apache/helix/manager/zk => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/callback}/ZkAsyncCallbacks.java (96%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/deprecated/IZkStateListener.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkBadVersionException.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkException.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkInterruptedException.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkMarshallingError.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkNoNodeException.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkNodeExistsException.java
 copy helix-core/src/main/java/org/apache/helix/task/beans/WorkflowBean.java => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkSessionMismatchedException.java (68%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkTimeoutException.java
 copy {helix-core/src/main/java/org/apache/helix/monitoring/mbeans => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/metric}/ZkClientMonitor.java (94%)
 copy {helix-core/src/main/java/org/apache/helix/monitoring/mbeans => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/metric}/ZkClientPathMonitor.java (98%)
 copy {helix-core/src/main/java/org/apache/helix/manager/zk => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize}/BasicZkSerializer.java (93%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/BytesPushThroughSerializer.java
 copy {helix-core/src/main/java/org/apache/helix/manager/zk => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize}/PathBasedZkSerializer.java (91%)
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/SerializableSerializer.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/TcclAwareObjectIputStream.java
 create mode 100644 zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/ZkSerializer.java
 copy {helix-core/src/main/java/org/apache/helix => zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient}/util/ExponentialBackoffStrategy.java (54%)
 create mode 100644 zookeeper-api/src/test/conf/testng.xml
 create mode 100644 zookeeper-api/src/test/java/org/apache/helix/zookeeper/constant/TestConstants.java
 create mode 100644 zookeeper-api/src/test/java/org/apache/helix/zookeeper/datamodel/serializer/TestZNRecordSerializeWriteSizeLimit.java
 create mode 100644 zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/ZkTestBase.java
 create mode 100644 zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientFactoryTestBase.java
 create mode 100644 zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientTestBase.java
 rename helix-core/src/main/java/org/apache/helix/HelixException.java => zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestDedicatedZkClient.java (54%)
 create mode 100644 zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestFederatedZkClient.java
 create mode 100644 zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestSharedZkClient.java
 create mode 100644 zookeeper-api/src/test/java/org/apache/helix/zookeeper/util/TestHttpRoutingDataReader.java
 copy {helix-rest => zookeeper-api}/src/test/resources/log4j.properties (100%)
 copy helix-core/helix-core-0.9.2-SNAPSHOT.ivy => zookeeper-api/zookeeper-api-0.9.2-SNAPSHOT.ivy (53%)


[helix] 17/19: Clean up code

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 8f9c5c591f88364c6a019f3c70ff207ca9bc50db
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Fri Feb 14 18:51:48 2020 -0800

    Clean up code
---
 .../main/java/org/apache/helix/lock/HelixLock.java |   8 +-
 .../main/java/org/apache/helix/lock/LockInfo.java  |  93 ++++-------------
 .../main/java/org/apache/helix/lock/LockScope.java |   7 ++
 .../apache/helix/lock/helix/HelixLockScope.java    | 115 +++------------------
 .../helix/lock/helix/ZKHelixNonblockingLock.java   |  63 ++---------
 .../lock/helix/TestZKHelixNonblockingLock.java     |  35 +++----
 6 files changed, 74 insertions(+), 247 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
index 1cd9e60..97e2c35 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
@@ -38,15 +38,15 @@ public interface HelixLock {
   boolean releaseLock();
 
   /**
-   * Retrieve the lock information, e.g. lock timeout, lock message, etc.
+   * Retrieve the information of the current lock on the resource this lock object specifies, e.g. lock timeout, lock message, etc.
    * @return lock metadata information
    */
-  LockInfo getLockInfo();
+  LockInfo getCurrentLockInfo();
 
   /**
-   * If the user is current lock owner
+   * If the user is current lock owner of the resource
    * @return true if the user is the lock owner,
    * false if the user is not the lock owner or the lock doesn't have a owner
    */
-  boolean isOwner();
+  boolean isCurrentOwner();
 }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
index 212c93c..250fb71 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
@@ -34,7 +34,8 @@ public class LockInfo extends HelixProperty {
   public static final long DEFAULT_TIMEOUT_LONG = -1L;
 
   // default lock info represents the status of a unlocked lock
-  public static final LockInfo defaultLockInfo = new LockInfo("");
+  public static final LockInfo defaultLockInfo =
+      new LockInfo(DEFAULT_OWNER_TEXT, DEFAULT_MESSAGE_TEXT, DEFAULT_TIMEOUT_LONG);
 
   /**
    * The keys to lock information
@@ -44,11 +45,11 @@ public class LockInfo extends HelixProperty {
   }
 
   /**
-   * Initialize a LockInfo with a ZNRecord id, set all info fields to default data
+   * Initialize a default LockInfo instance
    */
-  public LockInfo(String id) {
-    super(id);
-    resetLockInfo();
+  private LockInfo() {
+    super("LOCK");
+    setLockInfoFields(null, null, DEFAULT_TIMEOUT_LONG);
   }
 
   /**
@@ -56,8 +57,13 @@ public class LockInfo extends HelixProperty {
    * @param znRecord The ZNRecord contains lock node data that used to initialize the LockInfo
    */
   public LockInfo(ZNRecord znRecord) {
-    super(znRecord);
-    setNullLockInfoFieldsToDefault();
+    this();
+    if (znRecord != null) {
+      String ownerId = znRecord.getSimpleField(LockInfoAttribute.OWNER.name());
+      String message = znRecord.getSimpleField(LockInfoAttribute.MESSAGE.name());
+      long timeout = znRecord.getLongField(LockInfoAttribute.TIMEOUT.name(), DEFAULT_TIMEOUT_LONG);
+      setLockInfoFields(ownerId, message, timeout);
+    }
   }
 
   /**
@@ -67,45 +73,22 @@ public class LockInfo extends HelixProperty {
    * @param timeout value of TIMEOUT attribute
    */
   public LockInfo(String ownerId, String message, long timeout) {
-    this(ownerId);
+    this();
     setLockInfoFields(ownerId, message, timeout);
   }
 
   /**
-   * Build a LOCKINFO instance that represents an unlocked lock states
-   * @return the unlocked lock node LockInfo instance
-   */
-  public static LockInfo buildUnlockedLockInfo() {
-    return new LockInfo("");
-  }
-
-  /**
    * Set each field of lock info to user provided values if the values are not null, null values are set to default values
    * @param ownerId value of OWNER attribute
    * @param message value of MESSAGE attribute
    * @param timeout value of TIMEOUT attribute
    */
-  public void setLockInfoFields(String ownerId, String message, Long timeout) {
+  private void setLockInfoFields(String ownerId, String message, long timeout) {
     _record.setSimpleField(LockInfoAttribute.OWNER.name(),
         ownerId == null ? DEFAULT_OWNER_TEXT : ownerId);
     _record.setSimpleField(LockInfoAttribute.MESSAGE.name(),
         message == null ? DEFAULT_MESSAGE_TEXT : message);
-    _record.setLongField(LockInfoAttribute.TIMEOUT.name(),
-        timeout == null ? DEFAULT_TIMEOUT_LONG : timeout);
-  }
-
-  /**
-   * Set all null values to default values in LockInfo, keep non-null values
-   */
-  private void setNullLockInfoFieldsToDefault() {
-    setLockInfoFields(getOwner(), getMessage(), getTimeout());
-  }
-
-  /**
-   * Reset the lock info to unlocked lock state
-   */
-  public void resetLockInfo() {
-    setLockInfoFields(DEFAULT_OWNER_TEXT, DEFAULT_MESSAGE_TEXT, DEFAULT_TIMEOUT_LONG);
+    _record.setLongField(LockInfoAttribute.TIMEOUT.name(), timeout);
   }
 
   /**
@@ -135,46 +118,10 @@ public class LockInfo extends HelixProperty {
   }
 
   /**
-   * Get the value for OWNER attribute of the lock from a ZNRecord
-   * @return the owner id of the lock, empty string if there is no owner id set
-   */
-  public static String getOwner(ZNRecord znRecord) {
-    if (znRecord == null) {
-      return DEFAULT_OWNER_TEXT;
-    }
-    String owner = znRecord.getSimpleField(LockInfoAttribute.OWNER.name());
-    return owner == null ? DEFAULT_OWNER_TEXT : owner;
-  }
-
-  /**
-   * Get the value for MESSAGE attribute of the lock from a ZNRecord
-   * @return the message of the lock, empty string if there is no message set
-   */
-  public static String getMessage(ZNRecord znRecord) {
-    if (znRecord == null) {
-      return DEFAULT_MESSAGE_TEXT;
-    }
-    String message = znRecord.getSimpleField(LockInfoAttribute.MESSAGE.name());
-    return message == null ? DEFAULT_MESSAGE_TEXT : message;
-  }
-
-  /**
-   * Get the value for TIMEOUT attribute of the lock from a ZNRecord
-   * @return the expiring time of the lock, -1 if there is no timeout set
-   */
-  public static long getTimeout(ZNRecord znRecord) {
-    if (znRecord == null) {
-      return DEFAULT_TIMEOUT_LONG;
-    }
-    return znRecord.getLongField(LockInfoAttribute.TIMEOUT.name(), DEFAULT_TIMEOUT_LONG);
-  }
-
-  /**
-   * Check if the lock has a owner id set
-   * @return true if an owner id is set, false if not
+   * Check if a lock has timed out
+   * @return return true if the lock has timed out, otherwise return false.
    */
-  public static boolean ownerIdSet(ZNRecord znRecord) {
-    String ownerId = getOwner(znRecord);
-    return !ownerId.equals(DEFAULT_OWNER_TEXT);
+  public boolean hasNotExpired() {
+    return System.currentTimeMillis() < getTimeout();
   }
 }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/LockScope.java b/helix-lock/src/main/java/org/apache/helix/lock/LockScope.java
index ff2d329..0fa5cfc 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/LockScope.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/LockScope.java
@@ -19,7 +19,14 @@
 
 package org.apache.helix.lock;
 
+/**
+ * A predefined class to generate the lock path based on user input
+ */
 public interface LockScope {
 
+  /**
+   * Get the lock path
+   * @return the path of the lock
+   */
   String getPath();
 }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/helix/HelixLockScope.java b/helix-lock/src/main/java/org/apache/helix/lock/helix/HelixLockScope.java
index 77907ea..8eec472 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/helix/HelixLockScope.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/helix/HelixLockScope.java
@@ -22,6 +22,7 @@ package org.apache.helix.lock.helix;
 import java.util.List;
 
 import org.apache.helix.lock.LockScope;
+import org.apache.helix.model.HelixConfigScope;
 import org.apache.helix.util.StringTemplate;
 
 
@@ -35,13 +36,11 @@ public class HelixLockScope implements LockScope {
    */
   public enum LockScopeProperty {
 
-    CLUSTER(1),
+    CLUSTER(2),
 
     PARTICIPANT(2),
 
-    RESOURCE(3),
-
-    PARTITION(4);
+    RESOURCE(2);
 
     //the number of arguments required to generate a full path for the specific scope
     final int _pathArgNum;
@@ -61,14 +60,6 @@ public class HelixLockScope implements LockScope {
     public int getPathArgNum() {
       return _pathArgNum;
     }
-
-    /**
-     * Get the position of this argument from the input that used to generate the scope
-     * @return the number of position of value for this property in the list of keys input
-     */
-    public int getArgPos() {
-      return _pathArgNum - 1;
-    }
   }
 
   /**
@@ -76,108 +67,34 @@ public class HelixLockScope implements LockScope {
    */
   private static final StringTemplate template = new StringTemplate();
 
+  //TODO: Enrich the logic of path generation once we have a more detailed design
   static {
-    template.addEntry(LockScopeProperty.CLUSTER, 1, "/{clusterName}/LOCK");
+    template.addEntry(LockScopeProperty.CLUSTER, 2, "/{clusterName}/LOCK/CLUSTER/{clusterName}");
     template.addEntry(HelixLockScope.LockScopeProperty.PARTICIPANT, 2,
-        "/{clusterName}/LOCK/{participantName}");
-    template.addEntry(HelixLockScope.LockScopeProperty.RESOURCE, 3,
-        "/{clusterName}/LOCK/{participantName}/{resourceName}");
-    template.addEntry(HelixLockScope.LockScopeProperty.PARTITION, 4,
-        "/{clusterName}/LOCK/{participantName}/{resourceName}/{partitionName}");
+        "/{clusterName}/LOCK/PARTICIPANT/{participantName}");
+    template.addEntry(HelixLockScope.LockScopeProperty.RESOURCE, 2,
+        "/{clusterName}/LOCK/RESOURCE/{resourceName}");
   }
 
-  private final HelixLockScope.LockScopeProperty _type;
-  private final String _clusterName;
-  private final String _participantName;
-  private final String _resourceName;
-  private final String _partitionName;
-
-  private final String _zkPath;
+  private final String _path;
 
   /**
    * Initialize with a type of scope and unique identifiers
    * @param type the scope
-   * @param zkPathKeys keys identifying a ZNode location
+   * @param pathKeys keys identifying a ZNode location
    */
-  private HelixLockScope(HelixLockScope.LockScopeProperty type, List<String> zkPathKeys) {
+  public HelixLockScope(HelixLockScope.LockScopeProperty type, List<String> pathKeys) {
 
-    if (zkPathKeys.size() != type.getPathArgNum()) {
+    if (pathKeys.size() != type.getPathArgNum()) {
       throw new IllegalArgumentException(
           type + " requires " + type.getPathArgNum() + " arguments to get znode, but was: "
-              + zkPathKeys);
-    }
-
-    _type = type;
-
-    //Initialize the name fields for various scope
-    _clusterName = zkPathKeys.get(LockScopeProperty.CLUSTER.getArgPos());
-
-    if (type.getPathArgNum() >= LockScopeProperty.PARTICIPANT.getPathArgNum()) {
-      _participantName = zkPathKeys.get(LockScopeProperty.PARTICIPANT.getArgPos());
-    } else {
-      _participantName = null;
-    }
-
-    if (type.getPathArgNum() >= LockScopeProperty.RESOURCE.getPathArgNum()) {
-      _resourceName = zkPathKeys.get(LockScopeProperty.RESOURCE.getArgPos());
-    } else {
-      _resourceName = null;
-    }
-
-    if (type.getPathArgNum() >= LockScopeProperty.PARTITION.getPathArgNum()) {
-      _partitionName = zkPathKeys.get(LockScopeProperty.PARTITION.getArgPos());
-    } else {
-      _partitionName = null;
+              + pathKeys);
     }
-
-    _zkPath = template.instantiate(type, zkPathKeys.toArray(new String[0])).toUpperCase();
-  }
-
-  /**
-   * Get the scope
-   * @return the type of scope
-   */
-  public HelixLockScope.LockScopeProperty getType() {
-    return _type;
-  }
-
-  /**
-   * Get the cluster name if it exists
-   * @return the cluster name
-   */
-  public String getClusterName() {
-    return _clusterName;
-  }
-
-  /**
-   * Get the participant name if it exists
-   * @return the participant name
-   */
-  public String getParticipantName() {
-    return _participantName;
-  }
-
-  /**
-   * Get the resource name if it exists
-   * @return the resource name
-   */
-  public String getResourceName() {
-    return _resourceName;
-  }
-
-  /**
-   * Get the partition name if it exists
-   * @return the partition name
-   */
-  public String getPartitionName() {
-    return _partitionName;
+    _path = template.instantiate(type, pathKeys.toArray(new String[0]));
   }
 
   @Override
-  /**
-   * Get the path to the corresponding ZNode
-   * @return a Zookeeper path
-   */ public String getPath() {
-    return _zkPath;
+  public String getPath() {
+    return _path;
   }
 }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java
index 478be95..41de4a7 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java
@@ -70,6 +70,9 @@ public class ZKHelixNonblockingLock implements HelixLock {
   private ZKHelixNonblockingLock(String lockPath, String zkAddress, Long timeout, String lockMsg,
       String userId) {
     _lockPath = lockPath;
+    if (timeout < 0) {
+      throw new IllegalArgumentException("The expiration time cannot be negative.");
+    }
     _timeout = timeout;
     _lockMsg = lockMsg;
     _userId = userId;
@@ -87,10 +90,7 @@ public class ZKHelixNonblockingLock implements HelixLock {
     } else {
       deadline = System.currentTimeMillis() + _timeout;
     }
-    LockInfo lockInfo = new LockInfo(_userId);
-    lockInfo.setLockInfoFields(_userId, _lockMsg, deadline);
-
-    LockUpdater updater = new LockUpdater(lockInfo);
+    LockUpdater updater = new LockUpdater(new LockInfo(_userId, _lockMsg, deadline));
     return _baseDataAccessor.update(_lockPath, updater, AccessOption.PERSISTENT);
   }
 
@@ -102,57 +102,15 @@ public class ZKHelixNonblockingLock implements HelixLock {
   }
 
   @Override
-  public LockInfo getLockInfo() {
+  public LockInfo getCurrentLockInfo() {
     ZNRecord curLockInfo = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
     return new LockInfo(curLockInfo);
   }
 
   @Override
-  public boolean isOwner() {
-    LockInfo lockInfo = getLockInfo();
-    return userIdMatches(lockInfo) && !hasTimedOut(lockInfo);
-  }
-
-  /**
-   * Check if a lock has timed out
-   * @return return true if the lock has timed out, otherwise return false.
-   */
-  private boolean hasTimedOut(LockInfo lockInfo) {
-    return System.currentTimeMillis() >= lockInfo.getTimeout();
-  }
-
-  /**
-   * Check if a lock has timed out with lock information stored in a ZNRecord
-   * @return return true if the lock has timed out, otherwise return false.
-   */
-  private boolean hasTimedOut(ZNRecord znRecord) {
-    return System.currentTimeMillis() >= LockInfo.getTimeout(znRecord);
-  }
-
-  /**
-   * Check if the current user Id matches with the owner Id in a lock info
-   * @return return true if the two ids match, otherwise return false.
-   */
-  private boolean userIdMatches(LockInfo lockInfo) {
-    return lockInfo.getOwner().equals(_userId);
-  }
-
-  /**
-   * Check if a user id in the ZNRecord matches current user's id
-   * @param znRecord ZNRecord contains lock information
-   * @return true if the ids match, false if not or ZNRecord does not contain user id information
-   */
-  private boolean userIdMatches(ZNRecord znRecord) {
-    return LockInfo.getOwner(znRecord).equals(_userId);
-  }
-
-  /**
-   * Check if the lock node has a current owner
-   * @param znRecord Lock information in format of ZNRecord
-   * @return true if the lock has a current owner that the ownership has not be timed out, otherwise false
-   */
-  private boolean hasNonExpiredOwner(ZNRecord znRecord) {
-    return LockInfo.ownerIdSet(znRecord) && !hasTimedOut(znRecord);
+  public boolean isCurrentOwner() {
+    LockInfo lockInfo = getCurrentLockInfo();
+    return lockInfo.getOwner().equals(_userId) && lockInfo.hasNotExpired();
   }
 
   /**
@@ -172,8 +130,9 @@ public class ZKHelixNonblockingLock implements HelixLock {
     @Override
     public ZNRecord update(ZNRecord current) {
       // If no one owns the lock, allow the update
-      // If the lock owner id matches user id, allow the update
-      if (!hasNonExpiredOwner(current) || userIdMatches(current)) {
+      // If the user is the current lock owner, allow the update
+      LockInfo curLockInfo = new LockInfo(current);
+      if (!curLockInfo.hasNotExpired() || isCurrentOwner()) {
         return _record;
       }
       // For users who are not the lock owner and try to do an update on a lock that is held by someone else, exception thrown is to be caught by data accessor, and return false for the update
diff --git a/helix-lock/src/test/java/org/apache/helix/lock/helix/TestZKHelixNonblockingLock.java b/helix-lock/src/test/java/org/apache/helix/lock/helix/TestZKHelixNonblockingLock.java
index e79922d..39205af 100644
--- a/helix-lock/src/test/java/org/apache/helix/lock/helix/TestZKHelixNonblockingLock.java
+++ b/helix-lock/src/test/java/org/apache/helix/lock/helix/TestZKHelixNonblockingLock.java
@@ -57,14 +57,12 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
 
     List<String> pathKeys = new ArrayList<>();
     pathKeys.add(_clusterName);
-    pathKeys.add("participant_name");
-    pathKeys.add("resource_name");
-    pathKeys.add("partition_name");
+    pathKeys.add(_clusterName);
 
-    _participantScope = new HelixLockScope(HelixLockScope.LockScopeProperty.PARTITION, pathKeys);
+    _participantScope = new HelixLockScope(HelixLockScope.LockScopeProperty.CLUSTER, pathKeys);
     _lockPath = _participantScope.getPath();
-    _lock = new ZKHelixNonblockingLock(_participantScope, ZK_ADDR, Long.MAX_VALUE,
-        _lockMessage, _userId);
+    _lock = new ZKHelixNonblockingLock(_participantScope, ZK_ADDR, Long.MAX_VALUE, _lockMessage,
+        _userId);
   }
 
   @BeforeMethod
@@ -81,17 +79,16 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
     Assert.assertTrue(_gZkClient.exists(_lockPath));
 
     // Get lock information
-    LockInfo lockInfo = _lock.getLockInfo();
+    LockInfo lockInfo = _lock.getCurrentLockInfo();
     Assert.assertEquals(lockInfo.getOwner(), _userId);
-    Assert.assertEquals(lockInfo.getMessage(),
-        _lockMessage);
+    Assert.assertEquals(lockInfo.getMessage(), _lockMessage);
 
     // Check if the user is lock owner
-    Assert.assertTrue(_lock.isOwner());
+    Assert.assertTrue(_lock.isCurrentOwner());
 
     // Release lock
     _lock.releaseLock();
-    Assert.assertFalse(_lock.isOwner());
+    Assert.assertFalse(_lock.isCurrentOwner());
   }
 
   @Test
@@ -101,16 +98,16 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
     String fakeUserID = UUID.randomUUID().toString();
     ZNRecord fakeRecord = new ZNRecord(fakeUserID);
     fakeRecord.setSimpleField(LockInfo.LockInfoAttribute.OWNER.name(), fakeUserID);
-    fakeRecord.setSimpleField(LockInfo.LockInfoAttribute.TIMEOUT.name(),
-        String.valueOf(Long.MAX_VALUE));
+    fakeRecord
+        .setSimpleField(LockInfo.LockInfoAttribute.TIMEOUT.name(), String.valueOf(Long.MAX_VALUE));
     _gZkClient.create(_lockPath, fakeRecord, CreateMode.PERSISTENT);
 
     // Check if the user is lock owner
-    Assert.assertFalse(_lock.isOwner());
+    Assert.assertFalse(_lock.isCurrentOwner());
 
     // Acquire lock
     Assert.assertFalse(_lock.acquireLock());
-    Assert.assertFalse(_lock.isOwner());
+    Assert.assertFalse(_lock.isCurrentOwner());
 
     // Release lock
     Assert.assertFalse(_lock.releaseLock());
@@ -129,11 +126,11 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
 
     // Acquire lock
     Assert.assertTrue(_lock.acquireLock());
-    Assert.assertTrue(_lock.isOwner());
+    Assert.assertTrue(_lock.isCurrentOwner());
 
     // Release lock
     Assert.assertTrue(_lock.releaseLock());
-    Assert.assertFalse(_lock.isOwner());
+    Assert.assertFalse(_lock.isCurrentOwner());
   }
 
   @Test
@@ -141,8 +138,8 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
     List<Callable<Boolean>> threads = new ArrayList<>();
     for (int i = 0; i < 2; i++) {
       ZKHelixNonblockingLock lock =
-          new ZKHelixNonblockingLock(_participantScope, ZK_ADDR, Long.MAX_VALUE,
-              _lockMessage, UUID.randomUUID().toString());
+          new ZKHelixNonblockingLock(_participantScope, ZK_ADDR, Long.MAX_VALUE, _lockMessage,
+              UUID.randomUUID().toString());
       threads.add(new TestSimultaneousAcquireLock(lock));
     }
     Map<String, Boolean> resultMap = TestHelper.startThreadsConcurrently(threads, 1000);


[helix] 08/19: Fixed logic of release and isOwner

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit aa4d0157feaf82c2b6d2829216ccc79b60e4eb4c
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Tue Feb 4 10:41:13 2020 -0800

    Fixed logic of release and isOwner
---
 .../main/java/org/apache/helix/lock/HelixLock.java |   2 +-
 .../main/java/org/apache/helix/lock/LockInfo.java  |   9 +-
 .../apache/helix/lock/ZKHelixNonblockingLock.java  | 136 ++++++++++++++-------
 .../helix/lock/ZKHelixNonblockingLockInfo.java     |  45 +++----
 .../helix/lock/TestZKHelixNonblockingLock.java     | 106 ++++++----------
 5 files changed, 150 insertions(+), 148 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
index 71b1ca6..2d7e318 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
@@ -42,7 +42,7 @@ public interface HelixLock {
    * Retrieve the lock information, e.g. lock timeout, lock message, etc.
    * @return lock metadata information
    */
-  <T> LockInfo<T> getLockInfo();
+  <K, V> LockInfo<K, V> getLockInfo();
 
   /**
    * If the user is current lock owner
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
index 30322bb..afd4c00 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
@@ -24,9 +24,10 @@ import java.util.Map;
 
 /**
  * Generic interface for a map contains the Helix lock information
- * @param <T> The type of the LockInfo value
+ * @param <K> The type of the LockInfo key
+ * @param <V> the type of the LockInfo value
  */
-public interface LockInfo<T> {
+public interface LockInfo<K, V> {
 
   //TODO: add specific setter and getter for any field that is determined to be universal for all implementations of HelixLock
 
@@ -35,12 +36,12 @@ public interface LockInfo<T> {
    * @param infoKey the key of the LockInfo field
    * @param infoValue the value of the LockInfo field
    */
-  void setInfoValue(String infoKey, T infoValue);
+  void setInfoValue(K infoKey, V infoValue);
 
   /**
    * Get the value of a field in LockInfo
    * @param infoKey the key of the LockInfo field
    * @return the value of the field or null if this key does not exist
    */
-  T getInfoValue(String infoKey);
+  V getInfoValue(K infoKey);
 }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
index 5be84bb..44b5aa9 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
@@ -19,20 +19,18 @@
 
 package org.apache.helix.lock;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.UUID;
 
+import com.google.common.annotations.VisibleForTesting;
+import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
+import org.apache.helix.HelixException;
 import org.apache.helix.ZNRecord;
-import org.apache.helix.ZNRecordUpdater;
-import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.manager.zk.ZkClient;
 import org.apache.helix.manager.zk.client.HelixZkClient;
 import org.apache.helix.model.HelixConfigScope;
-import org.apache.helix.util.ZNRecordUtil;
 import org.apache.log4j.Logger;
 import org.apache.zookeeper.data.Stat;
 
@@ -45,8 +43,11 @@ public class ZKHelixNonblockingLock implements HelixLock {
   private static final Logger LOG = Logger.getLogger(ZKHelixNonblockingLock.class);
 
   private static final String LOCK_ROOT = "LOCKS";
+  private static final String PATH_DELIMITER = "/";
+  private static final String UUID_FORMAT_REGEX =
+      "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
   private final String _lockPath;
-  private final String _userID;
+  private final String _userId;
   private final long _timeout;
   private final String _lockMsg;
   private final BaseDataAccessor<ZNRecord> _baseDataAccessor;
@@ -58,10 +59,12 @@ public class ZKHelixNonblockingLock implements HelixLock {
    * @param zkAddress the zk address the cluster connects to
    * @param timeout the timeout period of the lcok
    * @param lockMsg the reason for having this lock
+   * @param userId a universal unique userId for lock owner identity
    */
   public ZKHelixNonblockingLock(String clusterName, HelixConfigScope scope, String zkAddress,
-      Long timeout, String lockMsg, String userID) {
-    this("/" + clusterName + '/' + LOCK_ROOT + '/' + scope, zkAddress, timeout, lockMsg, userID);
+      Long timeout, String lockMsg, String userId) {
+    this(PATH_DELIMITER + clusterName + PATH_DELIMITER + LOCK_ROOT + PATH_DELIMITER + scope,
+        zkAddress, timeout, lockMsg, userId);
   }
 
   /**
@@ -70,29 +73,36 @@ public class ZKHelixNonblockingLock implements HelixLock {
    * @param zkAddress the zk address of the cluster
    * @param timeout the timeout period of the lcok
    * @param lockMsg the reason for having this lock
+   * @param userId a universal unique userId for lock owner identity
    */
-  public ZKHelixNonblockingLock(String lockPath, String zkAddress, Long timeout, String lockMsg,
-      String userID) {
+  private ZKHelixNonblockingLock(String lockPath, String zkAddress, Long timeout, String lockMsg,
+      String userId) {
     HelixZkClient zkClient = new ZkClient(zkAddress);
     _lockPath = lockPath;
     _timeout = timeout;
     _lockMsg = lockMsg;
-    _userID = userID;
+    if (!userId.matches(UUID_FORMAT_REGEX)) {
+      throw new IllegalArgumentException("The input user id is not a valid UUID.");
+    }
+    _userId = userId;
     _baseDataAccessor = new ZkBaseDataAccessor<ZNRecord>(zkClient.getServers());
   }
 
   @Override
-  /**
-   * Blocking call to acquire a lock
-   * @return true if the lock was successfully acquired,
-   * false if the lock could not be acquired
-   */ public boolean acquireLock() {
+  public boolean acquireLock() {
 
     // Set lock information fields
-    ZNRecord lockInfo = new ZNRecord(_userID);
-    lockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name(), _userID);
+    ZNRecord lockInfo = new ZNRecord(_userId);
+    lockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name(), _userId);
     lockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE.name(), _lockMsg);
-    long timeout = System.currentTimeMillis() + _timeout;
+    long timeout;
+
+    // If the input timeout value is the max value, set the expire time to max value
+    if (_timeout == Long.MAX_VALUE) {
+      timeout = _timeout;
+    } else {
+      timeout = System.currentTimeMillis() + _timeout;
+    }
     lockInfo
         .setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(), String.valueOf(timeout));
 
@@ -106,6 +116,7 @@ public class ZKHelixNonblockingLock implements HelixLock {
       long curTimeout =
           Long.parseLong(curLock.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name()));
       if (System.currentTimeMillis() >= curTimeout) {
+        // set may fail when the znode version changes in between the get and set, meaning there are some changes in the lock
         success =
             _baseDataAccessor.set(_lockPath, lockInfo, stat.getVersion(), AccessOption.PERSISTENT);
       }
@@ -114,46 +125,79 @@ public class ZKHelixNonblockingLock implements HelixLock {
   }
 
   @Override
-  /**
-   * Blocking call to release a lock
-   * @return true if the lock was successfully released,
-   * false if the locked is not locked or is not locked by the user,
-   * or the lock could not be released
-   */ public boolean releaseLock() {
-    if (isOwner()) {
-      return _baseDataAccessor.remove(_lockPath, AccessOption.PERSISTENT);
-    }
-    return false;
+  public boolean releaseLock() {
+    ZNRecord newLockInfo = new ZNRecord(_userId);
+    newLockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name(),
+        ZKHelixNonblockingLockInfo.DEFAULT_OWNER_TEXT);
+    newLockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE.name(),
+        ZKHelixNonblockingLockInfo.DEFAULT_MESSAGE_TEXT);
+    newLockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(),
+        ZKHelixNonblockingLockInfo.DEFAULT_TIMEOUT_TEXT);
+    LockUpdater updater = new LockUpdater(newLockInfo);
+    return _baseDataAccessor.update(_lockPath, updater, AccessOption.PERSISTENT);
   }
 
   @Override
-  /**
-   * Retrieve the lock information, e.g. lock timeout, lock message, etc.
-   * @return lock metadata information, return null if there is no lock node for the path provided
-   */ public LockInfo<String> getLockInfo() {
+  public ZKHelixNonblockingLockInfo getLockInfo() {
     if (!_baseDataAccessor.exists(_lockPath, AccessOption.PERSISTENT)) {
-      return null;
+      return new ZKHelixNonblockingLockInfo();
     }
-    ZKHelixNonblockingLockInfo<String> lockInfo = new ZKHelixNonblockingLockInfo<>();
     ZNRecord curLockInfo = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
-    lockInfo.setLockInfoFields(curLockInfo);
-    return lockInfo;
+    return new ZKHelixNonblockingLockInfo(curLockInfo);
   }
 
   @Override
-  /**
-   * Check if the user is current lock owner
-   * @return true if the user is the lock owner,
-   * false if the user is not the lock owner or the lock doesn't have a owner
-   */ public boolean isOwner() {
+  public boolean isOwner() {
     ZNRecord curLockInfo = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
     if (curLockInfo == null) {
       return false;
     }
-    String ownerID = curLockInfo.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name());
-    if (ownerID == null) {
-      return false;
+    if (userIdMatches(curLockInfo)) {
+      return !hasTimedOut(curLockInfo);
+    }
+    return false;
+  }
+
+  /**
+   * Check if a lock has timed out
+   * @param record the current lock information in ZNRecord format
+   * @return return true if the lock has timed out, otherwise return false.
+   */
+  private boolean hasTimedOut(ZNRecord record) {
+    String timeoutStr = record.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name());
+    return System.currentTimeMillis() >= Long.parseLong(timeoutStr);
+  }
+
+  /**
+   * Check if the current user Id matches with the owner Id in a lock info
+   * @param record the lock information in ZNRecord format
+   * @return return true if the two ids match, otherwise return false.
+   */
+  private boolean userIdMatches(ZNRecord record) {
+    String ownerId = record.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name());
+    return ownerId.equals(_userId);
+  }
+
+  /**
+   * Class that specifies how a lock node should be updated with another lock node for a lock owner only
+   */
+  private class LockUpdater implements DataUpdater<ZNRecord> {
+    final ZNRecord _record;
+
+    /**
+     * Initialize a structure for lock owner to update a lock node value
+     * @param record the lock node value will be updated in ZNRecord format
+     */
+    public LockUpdater(ZNRecord record) {
+      _record = record;
+    }
+
+    @Override
+    public ZNRecord update(ZNRecord current) {
+      if (current != null && userIdMatches(current) && !hasTimedOut(current)) {
+        return _record;
+      }
+      throw new HelixException("User is not authorized to perform this operation.");
     }
-    return ownerID.equals(_userID);
   }
 }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
index fb86187..50daa03 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
@@ -25,45 +25,38 @@ import java.util.Map;
 import org.apache.helix.ZNRecord;
 
 
-public class ZKHelixNonblockingLockInfo<T extends String> implements LockInfo<T> {
+public class ZKHelixNonblockingLockInfo<K extends ZKHelixNonblockingLockInfo.InfoKey, V extends String> implements LockInfo<K, V> {
 
-  private Map<String, String> lockInfo;
+  public static final String DEFAULT_OWNER_TEXT = "";
+  public static final String DEFAULT_MESSAGE_TEXT = "";
+  public static final String DEFAULT_TIMEOUT_TEXT = String.valueOf(-1);
+  private Map<InfoKey, String> lockInfo;
 
-  enum InfoKey {
+  public enum InfoKey {
     OWNER, MESSAGE, TIMEOUT
   }
 
   public ZKHelixNonblockingLockInfo() {
     lockInfo = new HashMap<>();
+    lockInfo.put(InfoKey.OWNER, DEFAULT_OWNER_TEXT);
+    lockInfo.put(InfoKey.MESSAGE, DEFAULT_MESSAGE_TEXT);
+    lockInfo.put(InfoKey.TIMEOUT, DEFAULT_TIMEOUT_TEXT);
   }
 
-  @Override
-  /**
-   * Create a single filed of LockInfo, or update the value of the field if it already exists
-   * @param infoKey the key of the LockInfo field
-   * @param infoValue the value of the LockInfo field
-   */ public void setInfoValue(String infoKey, String infoValue) {
-    lockInfo.put(infoKey, infoValue);
+  public ZKHelixNonblockingLockInfo(ZNRecord znRecord) {
+    this();
+    lockInfo.put(InfoKey.OWNER, znRecord.getSimpleField(InfoKey.OWNER.name()));
+    lockInfo.put(InfoKey.MESSAGE, znRecord.getSimpleField(InfoKey.MESSAGE.name()));
+    lockInfo.put(InfoKey.TIMEOUT, znRecord.getSimpleField(InfoKey.TIMEOUT.name()));
   }
 
   @Override
-  /**
-   * Get the value of a field in LockInfo
-   * @param infoKey the key of the LockInfo field
-   * @return the value of the field or null if this key does not exist
-   */ public T getInfoValue(String infoKey) {
-    return (T) lockInfo.get(infoKey);
+  public void setInfoValue(InfoKey infoKey, String infoValue) {
+    lockInfo.put(infoKey, infoValue);
   }
 
-  /**
-   * Update the lock info with information in a ZNRecord
-   * @param record Information about the lock that stored as ZNRecord format
-   */
-  public void setLockInfoFields(ZNRecord record) {
-    if (record == null) {
-      return;
-    }
-    Map<String, String> recordSimpleFields = record.getSimpleFields();
-    lockInfo.putAll(recordSimpleFields);
+  @Override
+  public String getInfoValue(InfoKey infoKey) {
+    return lockInfo.get(infoKey);
   }
 }
diff --git a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
index 95c41ac..37daf7b 100644
--- a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
+++ b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
@@ -31,16 +31,19 @@ import org.apache.helix.model.builder.HelixConfigScopeBuilder;
 import org.apache.zookeeper.CreateMode;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 
 public class TestZKHelixNonblockingLock extends ZkUnitTestBase {
 
-  private final String _lockPath = "/testLockPath";
   private final String _className = TestHelper.getTestClassName();
   private final String _methodName = TestHelper.getTestMethodName();
   private final String _clusterName = _className + "_" + _methodName;
   private final String _lockMessage = "Test";
+  private String _lockPath;
+  private ZKHelixNonblockingLock _lock;
+  private String _userId;
 
   @BeforeClass
   public void beforeClass() throws Exception {
@@ -49,79 +52,47 @@ public class TestZKHelixNonblockingLock extends ZkUnitTestBase {
 
     TestHelper.setupCluster(_clusterName, ZK_ADDR, 12918, "localhost", "TestDB", 1, 10, 5, 3,
         "MasterSlave", true);
-  }
-
-  @Test
-  public void testAcquireLockWithParticipantScope() {
-
-    // Initialize lock with participant config scope
+    _userId = UUID.randomUUID().toString();
     HelixConfigScope participantScope =
         new HelixConfigScopeBuilder(ConfigScopeProperty.PARTICIPANT).forCluster(_clusterName)
             .forParticipant("localhost_12918").build();
 
-    String userID = UUID.randomUUID().toString();
-
-    ZKHelixNonblockingLock lock =
-        new ZKHelixNonblockingLock(_clusterName, participantScope, ZK_ADDR, Long.MAX_VALUE,
-            _lockMessage, userID);
+    _lockPath = "/" + _clusterName + '/' + "LOCKS" + '/' + participantScope;
+    _lock = new ZKHelixNonblockingLock(_clusterName, participantScope, ZK_ADDR, Long.MAX_VALUE,
+        _lockMessage, _userId);
 
-    // Acquire lock
-    lock.acquireLock();
-    String lockPath = "/" + _clusterName + '/' + "LOCKS" + '/' + participantScope;
-    Assert.assertTrue(_gZkClient.exists(lockPath));
-
-    // Get lock information
-    LockInfo<String> record = lock.getLockInfo();
-    Assert
-        .assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name()), userID);
-    Assert.assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE.name()),
-        _lockMessage);
-
-    // Check if the user is lock owner
-    Assert.assertTrue(lock.isOwner());
+  }
 
-    // Release lock
-    lock.releaseLock();
-    Assert.assertFalse(_gZkClient.exists(lockPath));
+  @BeforeMethod
+  public void beforeMethod() {
+    _gZkClient.delete(_lockPath);
+    Assert.assertFalse(_gZkClient.exists(_lockPath));
   }
 
   @Test
-  public void testAcquireLockWithUserProvidedPath() {
-
-    // Initialize lock with user provided path
-    String userID = UUID.randomUUID().toString();
-
-    ZKHelixNonblockingLock lock =
-        new ZKHelixNonblockingLock(_lockPath, ZK_ADDR, Long.MAX_VALUE, _lockMessage, userID);
+  public void testAcquireLock() {
 
-    //Acquire lock
-    lock.acquireLock();
+    // Acquire lock
+    _lock.acquireLock();
     Assert.assertTrue(_gZkClient.exists(_lockPath));
 
     // Get lock information
-    LockInfo<String> record = lock.getLockInfo();
-    Assert
-        .assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name()), userID);
-    Assert.assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE.name()),
+    LockInfo<ZKHelixNonblockingLockInfo.InfoKey, String> record = _lock.getLockInfo();
+    Assert.assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.OWNER), _userId);
+    Assert.assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE),
         _lockMessage);
 
     // Check if the user is lock owner
-    Assert.assertTrue(lock.isOwner());
+    Assert.assertTrue(_lock.isOwner());
 
     // Release lock
-    lock.releaseLock();
-    Assert.assertFalse(_gZkClient.exists(_lockPath));
+    _lock.releaseLock();
+    Assert.assertFalse(_lock.isOwner());
   }
 
   @Test
   public void testAcquireLockWhenExistingLockNotExpired() {
 
-    // Initialize lock with user provided path
-    String ownerID = UUID.randomUUID().toString();
-
-    ZKHelixNonblockingLock lock =
-        new ZKHelixNonblockingLock(_lockPath, ZK_ADDR, 0L, _lockMessage, ownerID);
-
     // Fake condition when the lock owner is not current user
     String fakeUserID = UUID.randomUUID().toString();
     ZNRecord fakeRecord = new ZNRecord(fakeUserID);
@@ -131,28 +102,19 @@ public class TestZKHelixNonblockingLock extends ZkUnitTestBase {
     _gZkClient.create(_lockPath, fakeRecord, CreateMode.PERSISTENT);
 
     // Check if the user is lock owner
-    Assert.assertFalse(lock.isOwner());
+    Assert.assertFalse(_lock.isOwner());
 
     // Acquire lock
-    Assert.assertFalse(lock.acquireLock());
-    Assert.assertFalse(lock.isOwner());
+    Assert.assertFalse(_lock.acquireLock());
+    Assert.assertFalse(_lock.isOwner());
 
     // Release lock
-    Assert.assertFalse(lock.releaseLock());
-    Assert.assertTrue(_gZkClient.exists(_lockPath));
-
-    _gZkClient.delete(_lockPath);
+    Assert.assertFalse(_lock.releaseLock());
   }
 
   @Test
   public void testAcquireLockWhenExistingLockExpired() {
 
-    // Initialize lock with user provided path
-    String ownerID = UUID.randomUUID().toString();
-
-    ZKHelixNonblockingLock lock =
-        new ZKHelixNonblockingLock(_lockPath, ZK_ADDR, Long.MAX_VALUE, _lockMessage, ownerID);
-
     // Fake condition when the current lock already expired
     String fakeUserID = UUID.randomUUID().toString();
     ZNRecord fakeRecord = new ZNRecord(fakeUserID);
@@ -162,15 +124,17 @@ public class TestZKHelixNonblockingLock extends ZkUnitTestBase {
     _gZkClient.create(_lockPath, fakeRecord, CreateMode.PERSISTENT);
 
     // Acquire lock
-    Assert.assertTrue(lock.acquireLock());
-    Assert.assertTrue(_gZkClient.exists(_lockPath));
-
-    // Check if the current user is the lock owner
-    Assert.assertTrue(lock.isOwner());
+    Assert.assertTrue(_lock.acquireLock());
+    Assert.assertTrue(_lock.isOwner());
 
     // Release lock
-    Assert.assertTrue(lock.releaseLock());
-    Assert.assertFalse(_gZkClient.exists(_lockPath));
+    Assert.assertTrue(_lock.releaseLock());
+    Assert.assertFalse(_lock.isOwner());
+  }
+
+  @Test
+  public void testSimultaneousAcquire() {
+
   }
 }
 


[helix] 05/19: Added details in comments

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 0cea09d97b3f63ed2b22a5fee521f2e7dc0b93a0
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Thu Jan 30 09:51:55 2020 -0800

    Added details in comments
---
 helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
index d163718..01ef63b 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
@@ -20,7 +20,7 @@ package org.apache.helix.lock;
  */
 
 /**
- * Generic interface for Helix distributed lock for both nonblocking and blocking calls
+ * Generic interface for Helix distributed lock
  */
 public interface HelixLock<T> {
   /**


[helix] 07/19: Added unit tests for Helix nonblocking lock

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit c9bdeab8d62a0ea08bd8ba3b760a234705de392f
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Mon Feb 3 14:26:41 2020 -0800

    Added unit tests for Helix nonblocking lock
---
 .../apache/helix/lock/ZKHelixNonblockingLock.java  |  50 ++++--
 .../helix/lock/ZKHelixNonblockingLockInfo.java     |  27 +++-
 .../helix/lock/TestZKHelixNonblockingLock.java     | 176 +++++++++++++++++++++
 3 files changed, 236 insertions(+), 17 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
index 75dd0e5..5be84bb 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
@@ -26,6 +26,7 @@ import java.util.UUID;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.ZNRecord;
+import org.apache.helix.ZNRecordUpdater;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.manager.zk.ZkClient;
@@ -33,6 +34,7 @@ import org.apache.helix.manager.zk.client.HelixZkClient;
 import org.apache.helix.model.HelixConfigScope;
 import org.apache.helix.util.ZNRecordUtil;
 import org.apache.log4j.Logger;
+import org.apache.zookeeper.data.Stat;
 
 
 /**
@@ -80,32 +82,44 @@ public class ZKHelixNonblockingLock implements HelixLock {
   }
 
   @Override
-  public boolean acquireLock() {
+  /**
+   * Blocking call to acquire a lock
+   * @return true if the lock was successfully acquired,
+   * false if the lock could not be acquired
+   */ public boolean acquireLock() {
 
     // Set lock information fields
     ZNRecord lockInfo = new ZNRecord(_userID);
-    lockInfo.setSimpleField("Owner", _userID);
-    lockInfo.setSimpleField("message", _lockMsg);
+    lockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name(), _userID);
+    lockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE.name(), _lockMsg);
     long timeout = System.currentTimeMillis() + _timeout;
-    lockInfo.setSimpleField("timeout", String.valueOf(timeout));
+    lockInfo
+        .setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(), String.valueOf(timeout));
 
     // Try to create the lock node
     boolean success = _baseDataAccessor.create(_lockPath, lockInfo, AccessOption.PERSISTENT);
 
-    // If fail to create the lock node, compare the timeout timestamp of current lock node with current time, if already passes the timeout, delete current lock node and try to create lock node again
+    // If fail to create the lock node (acquire the lock), compare the timeout timestamp of current lock node with current time, if already passes the timeout, release current lock and try to acquire the lock again
     if (!success) {
-      ZNRecord curLock = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
-      long curTimeout = Long.parseLong(curLock.getSimpleField("timeout"));
+      Stat stat = new Stat();
+      ZNRecord curLock = _baseDataAccessor.get(_lockPath, stat, AccessOption.PERSISTENT);
+      long curTimeout =
+          Long.parseLong(curLock.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name()));
       if (System.currentTimeMillis() >= curTimeout) {
-        _baseDataAccessor.remove(_lockPath, AccessOption.PERSISTENT);
-        return _baseDataAccessor.create(_lockPath, lockInfo, AccessOption.PERSISTENT);
+        success =
+            _baseDataAccessor.set(_lockPath, lockInfo, stat.getVersion(), AccessOption.PERSISTENT);
       }
     }
     return success;
   }
 
   @Override
-  public boolean releaseLock() {
+  /**
+   * Blocking call to release a lock
+   * @return true if the lock was successfully released,
+   * false if the locked is not locked or is not locked by the user,
+   * or the lock could not be released
+   */ public boolean releaseLock() {
     if (isOwner()) {
       return _baseDataAccessor.remove(_lockPath, AccessOption.PERSISTENT);
     }
@@ -113,7 +127,13 @@ public class ZKHelixNonblockingLock implements HelixLock {
   }
 
   @Override
-  public LockInfo<String> getLockInfo() {
+  /**
+   * Retrieve the lock information, e.g. lock timeout, lock message, etc.
+   * @return lock metadata information, return null if there is no lock node for the path provided
+   */ public LockInfo<String> getLockInfo() {
+    if (!_baseDataAccessor.exists(_lockPath, AccessOption.PERSISTENT)) {
+      return null;
+    }
     ZKHelixNonblockingLockInfo<String> lockInfo = new ZKHelixNonblockingLockInfo<>();
     ZNRecord curLockInfo = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
     lockInfo.setLockInfoFields(curLockInfo);
@@ -121,12 +141,16 @@ public class ZKHelixNonblockingLock implements HelixLock {
   }
 
   @Override
-  public boolean isOwner() {
+  /**
+   * Check if the user is current lock owner
+   * @return true if the user is the lock owner,
+   * false if the user is not the lock owner or the lock doesn't have a owner
+   */ public boolean isOwner() {
     ZNRecord curLockInfo = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
     if (curLockInfo == null) {
       return false;
     }
-    String ownerID = curLockInfo.getSimpleField("owner");
+    String ownerID = curLockInfo.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name());
     if (ownerID == null) {
       return false;
     }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
index d54c386..fb86187 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
@@ -27,23 +27,42 @@ import org.apache.helix.ZNRecord;
 
 public class ZKHelixNonblockingLockInfo<T extends String> implements LockInfo<T> {
 
-  private Map<String, String>  lockInfo;
+  private Map<String, String> lockInfo;
+
+  enum InfoKey {
+    OWNER, MESSAGE, TIMEOUT
+  }
 
   public ZKHelixNonblockingLockInfo() {
     lockInfo = new HashMap<>();
   }
 
   @Override
-  public void setInfoValue(String infoKey, String infoValue) {
+  /**
+   * Create a single filed of LockInfo, or update the value of the field if it already exists
+   * @param infoKey the key of the LockInfo field
+   * @param infoValue the value of the LockInfo field
+   */ public void setInfoValue(String infoKey, String infoValue) {
     lockInfo.put(infoKey, infoValue);
   }
 
   @Override
-  public T getInfoValue(String infoKey) {
-    return (T)lockInfo.get(infoKey);
+  /**
+   * Get the value of a field in LockInfo
+   * @param infoKey the key of the LockInfo field
+   * @return the value of the field or null if this key does not exist
+   */ public T getInfoValue(String infoKey) {
+    return (T) lockInfo.get(infoKey);
   }
 
+  /**
+   * Update the lock info with information in a ZNRecord
+   * @param record Information about the lock that stored as ZNRecord format
+   */
   public void setLockInfoFields(ZNRecord record) {
+    if (record == null) {
+      return;
+    }
     Map<String, String> recordSimpleFields = record.getSimpleFields();
     lockInfo.putAll(recordSimpleFields);
   }
diff --git a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
new file mode 100644
index 0000000..95c41ac
--- /dev/null
+++ b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+package org.apache.helix.lock;
+
+import java.util.Date;
+import java.util.UUID;
+
+import org.apache.helix.TestHelper;
+import org.apache.helix.ZNRecord;
+import org.apache.helix.ZkUnitTestBase;
+import org.apache.helix.model.HelixConfigScope;
+import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty;
+import org.apache.helix.model.builder.HelixConfigScopeBuilder;
+import org.apache.zookeeper.CreateMode;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+
+public class TestZKHelixNonblockingLock extends ZkUnitTestBase {
+
+  private final String _lockPath = "/testLockPath";
+  private final String _className = TestHelper.getTestClassName();
+  private final String _methodName = TestHelper.getTestMethodName();
+  private final String _clusterName = _className + "_" + _methodName;
+  private final String _lockMessage = "Test";
+
+  @BeforeClass
+  public void beforeClass() throws Exception {
+
+    System.out.println("START " + _clusterName + " at " + new Date(System.currentTimeMillis()));
+
+    TestHelper.setupCluster(_clusterName, ZK_ADDR, 12918, "localhost", "TestDB", 1, 10, 5, 3,
+        "MasterSlave", true);
+  }
+
+  @Test
+  public void testAcquireLockWithParticipantScope() {
+
+    // Initialize lock with participant config scope
+    HelixConfigScope participantScope =
+        new HelixConfigScopeBuilder(ConfigScopeProperty.PARTICIPANT).forCluster(_clusterName)
+            .forParticipant("localhost_12918").build();
+
+    String userID = UUID.randomUUID().toString();
+
+    ZKHelixNonblockingLock lock =
+        new ZKHelixNonblockingLock(_clusterName, participantScope, ZK_ADDR, Long.MAX_VALUE,
+            _lockMessage, userID);
+
+    // Acquire lock
+    lock.acquireLock();
+    String lockPath = "/" + _clusterName + '/' + "LOCKS" + '/' + participantScope;
+    Assert.assertTrue(_gZkClient.exists(lockPath));
+
+    // Get lock information
+    LockInfo<String> record = lock.getLockInfo();
+    Assert
+        .assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name()), userID);
+    Assert.assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE.name()),
+        _lockMessage);
+
+    // Check if the user is lock owner
+    Assert.assertTrue(lock.isOwner());
+
+    // Release lock
+    lock.releaseLock();
+    Assert.assertFalse(_gZkClient.exists(lockPath));
+  }
+
+  @Test
+  public void testAcquireLockWithUserProvidedPath() {
+
+    // Initialize lock with user provided path
+    String userID = UUID.randomUUID().toString();
+
+    ZKHelixNonblockingLock lock =
+        new ZKHelixNonblockingLock(_lockPath, ZK_ADDR, Long.MAX_VALUE, _lockMessage, userID);
+
+    //Acquire lock
+    lock.acquireLock();
+    Assert.assertTrue(_gZkClient.exists(_lockPath));
+
+    // Get lock information
+    LockInfo<String> record = lock.getLockInfo();
+    Assert
+        .assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name()), userID);
+    Assert.assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE.name()),
+        _lockMessage);
+
+    // Check if the user is lock owner
+    Assert.assertTrue(lock.isOwner());
+
+    // Release lock
+    lock.releaseLock();
+    Assert.assertFalse(_gZkClient.exists(_lockPath));
+  }
+
+  @Test
+  public void testAcquireLockWhenExistingLockNotExpired() {
+
+    // Initialize lock with user provided path
+    String ownerID = UUID.randomUUID().toString();
+
+    ZKHelixNonblockingLock lock =
+        new ZKHelixNonblockingLock(_lockPath, ZK_ADDR, 0L, _lockMessage, ownerID);
+
+    // Fake condition when the lock owner is not current user
+    String fakeUserID = UUID.randomUUID().toString();
+    ZNRecord fakeRecord = new ZNRecord(fakeUserID);
+    fakeRecord.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name(), fakeUserID);
+    fakeRecord.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(),
+        String.valueOf(Long.MAX_VALUE));
+    _gZkClient.create(_lockPath, fakeRecord, CreateMode.PERSISTENT);
+
+    // Check if the user is lock owner
+    Assert.assertFalse(lock.isOwner());
+
+    // Acquire lock
+    Assert.assertFalse(lock.acquireLock());
+    Assert.assertFalse(lock.isOwner());
+
+    // Release lock
+    Assert.assertFalse(lock.releaseLock());
+    Assert.assertTrue(_gZkClient.exists(_lockPath));
+
+    _gZkClient.delete(_lockPath);
+  }
+
+  @Test
+  public void testAcquireLockWhenExistingLockExpired() {
+
+    // Initialize lock with user provided path
+    String ownerID = UUID.randomUUID().toString();
+
+    ZKHelixNonblockingLock lock =
+        new ZKHelixNonblockingLock(_lockPath, ZK_ADDR, Long.MAX_VALUE, _lockMessage, ownerID);
+
+    // Fake condition when the current lock already expired
+    String fakeUserID = UUID.randomUUID().toString();
+    ZNRecord fakeRecord = new ZNRecord(fakeUserID);
+    fakeRecord.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name(), fakeUserID);
+    fakeRecord.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(),
+        String.valueOf(System.currentTimeMillis()));
+    _gZkClient.create(_lockPath, fakeRecord, CreateMode.PERSISTENT);
+
+    // Acquire lock
+    Assert.assertTrue(lock.acquireLock());
+    Assert.assertTrue(_gZkClient.exists(_lockPath));
+
+    // Check if the current user is the lock owner
+    Assert.assertTrue(lock.isOwner());
+
+    // Release lock
+    Assert.assertTrue(lock.releaseLock());
+    Assert.assertFalse(_gZkClient.exists(_lockPath));
+  }
+}
+


[helix] 01/19: Add Helix Distributed lock module (#673)

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 2417fe69320f45b2cfc23cf066335e78647e9831
Author: zhangmeng916 <56...@users.noreply.github.com>
AuthorDate: Tue Jan 14 17:11:25 2020 -0800

    Add Helix Distributed lock module (#673)
    
    Add Helix Lock module.
---
 bump-up.command                                |  17 ++
 helix-lock/LICENSE                             | 273 +++++++++++++++++++++++++
 helix-lock/NOTICE                              |  37 ++++
 helix-lock/helix-lock-0.9.2-SNAPSHOT.ivy       |  51 +++++
 helix-lock/pom.xml                             | 112 ++++++++++
 helix-lock/src/assemble/assembly.xml           |  60 ++++++
 helix-lock/src/main/config/log4j.properties    |  32 +++
 helix-lock/src/test/conf/testng.xml            |  27 +++
 helix-lock/src/test/resources/log4j.properties |  41 ++++
 pom.xml                                        |   1 +
 10 files changed, 651 insertions(+)

diff --git a/bump-up.command b/bump-up.command
index eed4ba4..2957f95 100755
--- a/bump-up.command
+++ b/bump-up.command
@@ -162,6 +162,23 @@ else
   echo "helix-rest/$ivy_file not exist"
 fi
 
+echo "bump up helix-lock/pom.xml"
+sed -i "s/${version}/${new_version}/g" helix-lock/pom.xml
+grep -C 1 "$new_version" helix-lock/pom.xml
+# git diff helix-lock/pom.xml
+
+ivy_file="helix-lock-"$version".ivy"
+new_ivy_file="helix-lock-"$new_version".ivy"
+# echo "$ivy_file"
+if [ -f helix-lock/$ivy_file ]; then
+  echo "bump up helix-lock/$ivy_file"
+  git mv "helix-lock/$ivy_file" "helix-lock/$new_ivy_file"
+  sed -i "s/${version}/${new_version}/g" "helix-lock/$new_ivy_file"
+  grep -C 1 "$new_version" "helix-lock/$new_ivy_file"
+else
+  echo "helix-lock/$ivy_file not exist"
+fi
+
 echo "bump up helix-agent/pom.xml"
 sed -i "s/${version}/${new_version}/g" helix-agent/pom.xml
 grep -C 1 "$new_version" helix-agent/pom.xml
diff --git a/helix-lock/LICENSE b/helix-lock/LICENSE
new file mode 100644
index 0000000..413913f
--- /dev/null
+++ b/helix-lock/LICENSE
@@ -0,0 +1,273 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
+
+
+
+For xstream:
+
+Copyright (c) 2003-2006, Joe Walnes
+Copyright (c) 2006-2009, 2011 XStream Committers
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of
+conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of
+conditions and the following disclaimer in the documentation and/or other materials provided
+with the distribution.
+
+3. Neither the name of XStream nor the names of its contributors may be used to endorse
+or promote products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+for jline:
+
+Copyright (c) 2002-2006, Marc Prud'hommeaux <mw...@cornell.edu>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with
+the distribution.
+
+Neither the name of JLine nor the names of its contributors
+may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
diff --git a/helix-lock/NOTICE b/helix-lock/NOTICE
new file mode 100644
index 0000000..9dc340b
--- /dev/null
+++ b/helix-lock/NOTICE
@@ -0,0 +1,37 @@
+Apache Helix
+Copyright 2014 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Codehaus (http://www.codehaus.org/).
+Licensed under the BSD License.
+
+This product includes software developed at
+jline (http://jline.sourceforge.net/).
+Licensed under the BSD License.
+
+This product includes software developed at
+restlet (http://www.restlet.org/about/legal).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Google (http://www.google.com/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+snakeyaml (http://www.snakeyaml.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+zkclient (https://github.com/sgroschupf/zkclient).
+Licensed under the Apache License 2.0.
+
+II. License Summary
+- Apache License 2.0
+- BSD License
diff --git a/helix-lock/helix-lock-0.9.2-SNAPSHOT.ivy b/helix-lock/helix-lock-0.9.2-SNAPSHOT.ivy
new file mode 100644
index 0000000..1ec1822
--- /dev/null
+++ b/helix-lock/helix-lock-0.9.2-SNAPSHOT.ivy
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<ivy-module version="1.0">
+	<info organisation="org.apache.helix"
+		module="helix-lock"
+		revision="0.9.2-SNAPSHOT"
+		status="integration"
+		publication="20200114115923"
+	/>
+	<configurations>
+		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
+		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
+		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
+		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
+		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
+		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases."/>
+		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
+	</configurations>
+	<publications>
+		<artifact name="helix-lock" type="jar" ext="jar" conf="master"/>
+	</publications>
+	<dependencies>
+	  <dependency org="org.slf4j" name="slf4j-api" rev="1.7.25" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)">
+        <artifact name="slf4j-api" ext="jar"/>
+    </dependency>
+    <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.7.14" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)">
+        <artifact name="slf4j-log4j12" ext="jar"/>
+    </dependency>
+    <dependency org="org.yaml" name="snakeyaml" rev="1.17">
+        <artifact name="snakeyaml" m:classifier="sources" ext="jar"/>
+    </dependency>
+		<dependency org="org.apache.helix" name="helix-core" rev="0.9.2-SNAPSHOT" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/>
+	</dependencies>
+</ivy-module>
diff --git a/helix-lock/pom.xml b/helix-lock/pom.xml
new file mode 100644
index 0000000..e36970e
--- /dev/null
+++ b/helix-lock/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <groupId>org.apache.helix</groupId>
+    <artifactId>helix</artifactId>
+    <version>0.9.2-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>helix-lock</artifactId>
+  <packaging>bundle</packaging>
+  <name>Apache Helix :: Distributed Lock</name>
+
+  <properties>
+    <osgi.import>
+      org.apache.helix*,
+      org.slf4j*;version="[1.6,2)",
+      *
+    </osgi.import>
+    <osgi.export>org.apache.helix.lock*;version="${project.version};-noimport:=true</osgi.export>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.yaml</groupId>
+      <artifactId>snakeyaml</artifactId>
+      <version>1.17</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.helix</groupId>
+      <artifactId>helix-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>3.8.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+      <version>4.5.8</version>
+    </dependency>
+    <dependency>
+      <groupId>com.thoughtworks.xstream</groupId>
+      <artifactId>xstream</artifactId>
+      <version>1.3.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.helix</groupId>
+      <artifactId>helix-core</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <resources>
+      <resource>
+        <directory>${basedir}</directory>
+        <includes>
+          <include>DISCLAIMER</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <descriptors>
+            <descriptor>src/assemble/assembly.xml</descriptor>
+          </descriptors>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/helix-lock/src/assemble/assembly.xml b/helix-lock/src/assemble/assembly.xml
new file mode 100644
index 0000000..a6bec12
--- /dev/null
+++ b/helix-lock/src/assemble/assembly.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<assembly>
+  <id>pkg</id>
+  <formats>
+    <format>tar</format>
+  </formats>
+  <fileSets>
+    <fileSet>
+      <directory>target/helix-lock-pkg/bin</directory>
+      <outputDirectory>bin</outputDirectory>
+      <lineEnding>unix</lineEnding>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+    </fileSet>
+    <fileSet>
+      <directory>target/helix-lock-pkg/repo/</directory>
+      <outputDirectory>repo</outputDirectory>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+      <excludes>
+        <exclude>**/*.xml</exclude>
+      </excludes>
+    </fileSet>
+   <fileSet>
+      <directory>target/helix-lock-pkg/conf</directory>
+      <outputDirectory>conf</outputDirectory>
+      <lineEnding>unix</lineEnding>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+    </fileSet>
+    <fileSet>
+      <directory>${project.basedir}</directory>
+      <outputDirectory>/</outputDirectory>
+      <includes>
+        <include>LICENSE</include>
+        <include>NOTICE</include>
+        <include>DISCLAIMER</include>
+      </includes>
+      <fileMode>0755</fileMode>
+    </fileSet>
+  </fileSets>
+</assembly>
diff --git a/helix-lock/src/main/config/log4j.properties b/helix-lock/src/main/config/log4j.properties
new file mode 100644
index 0000000..f52dab8
--- /dev/null
+++ b/helix-lock/src/main/config/log4j.properties
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+
+# Set root logger level to INFO and its only appender to A1.
+log4j.rootLogger=INFO,A1
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
+
+log4j.logger.org.I0Itec=ERROR
+log4j.logger.org.apache.zookeeper=ERROR
+log4j.logger.org.apache.helix=INFO
diff --git a/helix-lock/src/test/conf/testng.xml b/helix-lock/src/test/conf/testng.xml
new file mode 100644
index 0000000..ec3c8dc
--- /dev/null
+++ b/helix-lock/src/test/conf/testng.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Suite" parallel="false">
+  <test name="Test" preserve-order="true">
+    <packages>
+      <package name="org.apache.helix.lock.*"/>
+    </packages>
+  </test>
+</suite>
diff --git a/helix-lock/src/test/resources/log4j.properties b/helix-lock/src/test/resources/log4j.properties
new file mode 100644
index 0000000..24b6d10
--- /dev/null
+++ b/helix-lock/src/test/resources/log4j.properties
@@ -0,0 +1,41 @@
+#
+# 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.
+#
+# Set root logger level to DEBUG and its only appender to R.
+log4j.rootLogger=ERROR, C
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.C=org.apache.log4j.ConsoleAppender
+log4j.appender.C.layout=org.apache.log4j.PatternLayout
+log4j.appender.C.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
+
+log4j.appender.R=org.apache.log4j.RollingFileAppender
+log4j.appender.R.layout=org.apache.log4j.PatternLayout
+log4j.appender.R.layout.ConversionPattern=%5p [%C:%M] (%F:%L) - %m%n
+log4j.appender.R.File=target/ClusterManagerLogs/log.txt
+
+log4j.appender.STATUSDUMP=org.apache.log4j.RollingFileAppender
+log4j.appender.STATUSDUMP.layout=org.apache.log4j.SimpleLayout
+log4j.appender.STATUSDUMP.File=target/ClusterManagerLogs/statusUpdates.log
+
+log4j.logger.org.I0Itec=ERROR
+log4j.logger.org.apache=ERROR
+log4j.logger.com.noelios=ERROR
+log4j.logger.org.restlet=ERROR
+
+log4j.logger.org.apache.helix.monitoring.ZKPathDataDumpTask=ERROR,STATUSDUMP
diff --git a/pom.xml b/pom.xml
index 60f8d87..04d0039 100644
--- a/pom.xml
+++ b/pom.xml
@@ -253,6 +253,7 @@ under the License.
     <module>helix-core</module>
     <module>helix-admin-webapp</module>
     <module>helix-rest</module>
+    <module>helix-lock</module>
     <module>helix-agent</module>
     <!--<module>helix-front</module>-->
     <module>recipes</module>


[helix] 09/19: Added test to acquire lock simultaneously

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit b53a94c9f72935ca441a1d546a6bc7bff47a8ca3
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Tue Feb 4 17:38:13 2020 -0800

    Added test to acquire lock simultaneously
---
 .../apache/helix/lock/ZKHelixNonblockingLock.java  |  5 +--
 .../helix/lock/TestZKHelixNonblockingLock.java     | 39 +++++++++++++++++++---
 2 files changed, 36 insertions(+), 8 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
index 44b5aa9..57ff9aa 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
@@ -152,10 +152,7 @@ public class ZKHelixNonblockingLock implements HelixLock {
     if (curLockInfo == null) {
       return false;
     }
-    if (userIdMatches(curLockInfo)) {
-      return !hasTimedOut(curLockInfo);
-    }
-    return false;
+    return userIdMatches(curLockInfo) && !hasTimedOut(curLockInfo);
   }
 
   /**
diff --git a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
index 37daf7b..f2308c1 100644
--- a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
+++ b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
@@ -19,12 +19,21 @@
 
 package org.apache.helix.lock;
 
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
+import java.util.Map;
 import java.util.UUID;
+import java.util.concurrent.Callable;
 
+import org.apache.helix.AccessOption;
+import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
 import org.apache.helix.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
+import org.apache.helix.manager.zk.TestWtCacheAsyncOpMultiThread;
+import org.apache.helix.manager.zk.ZKHelixDataAccessor;
+import org.apache.helix.manager.zk.ZkCacheBaseDataAccessor;
 import org.apache.helix.model.HelixConfigScope;
 import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty;
 import org.apache.helix.model.builder.HelixConfigScopeBuilder;
@@ -44,6 +53,7 @@ public class TestZKHelixNonblockingLock extends ZkUnitTestBase {
   private String _lockPath;
   private ZKHelixNonblockingLock _lock;
   private String _userId;
+  private HelixConfigScope _participantScope;
 
   @BeforeClass
   public void beforeClass() throws Exception {
@@ -53,14 +63,13 @@ public class TestZKHelixNonblockingLock extends ZkUnitTestBase {
     TestHelper.setupCluster(_clusterName, ZK_ADDR, 12918, "localhost", "TestDB", 1, 10, 5, 3,
         "MasterSlave", true);
     _userId = UUID.randomUUID().toString();
-    HelixConfigScope participantScope =
+    _participantScope =
         new HelixConfigScopeBuilder(ConfigScopeProperty.PARTICIPANT).forCluster(_clusterName)
             .forParticipant("localhost_12918").build();
 
-    _lockPath = "/" + _clusterName + '/' + "LOCKS" + '/' + participantScope;
-    _lock = new ZKHelixNonblockingLock(_clusterName, participantScope, ZK_ADDR, Long.MAX_VALUE,
+    _lockPath = "/" + _clusterName + '/' + "LOCKS" + '/' + _participantScope;
+    _lock = new ZKHelixNonblockingLock(_clusterName, _participantScope, ZK_ADDR, Long.MAX_VALUE,
         _lockMessage, _userId);
-
   }
 
   @BeforeMethod
@@ -134,7 +143,29 @@ public class TestZKHelixNonblockingLock extends ZkUnitTestBase {
 
   @Test
   public void testSimultaneousAcquire() {
+    List<Callable<Boolean>> threads = new ArrayList<>();
+    for (int i = 0; i < 2; i++) {
+      ZKHelixNonblockingLock lock =
+          new ZKHelixNonblockingLock(_clusterName, _participantScope, ZK_ADDR, Long.MAX_VALUE,
+              _lockMessage, UUID.randomUUID().toString());
+      threads.add(new TestSimultaneousAcquireLock(lock));
+    }
+    Map<String, Boolean> resultMap = TestHelper.startThreadsConcurrently(threads, 1000);
+    Assert.assertEquals(resultMap.size(), 2);
+    Assert.assertEqualsNoOrder(resultMap.values().toArray(), new Boolean[]{true, false});
+  }
+
+  private static class TestSimultaneousAcquireLock implements Callable<Boolean> {
+    final ZKHelixNonblockingLock _lock;
+
+    TestSimultaneousAcquireLock(ZKHelixNonblockingLock lock) {
+      _lock = lock;
+    }
 
+    @Override
+    public Boolean call() throws Exception {
+      return _lock.acquireLock();
+    }
   }
 }
 


[helix] 03/19: Created Helix distributed lock design (apache#702)

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 593042987202fbfa7265219d3f353bcdf393ceb8
Author: Molly Gao <mg...@mgao-ld1.linkedin.biz>
AuthorDate: Thu Jan 23 11:30:59 2020 -0800

    Created Helix distributed lock design (apache#702)
---
 helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java | 1 +
 1 file changed, 1 insertion(+)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
index 01ef63b..a04ac01 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
@@ -22,6 +22,7 @@ package org.apache.helix.lock;
 /**
  * Generic interface for Helix distributed lock
  */
+<<<<<<< HEAD
 public interface HelixLock<T> {
   /**
    * Blocking call to acquire a lock


[helix] 13/19: simplified acquireLock logic

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit ed53139414dafbe154488bb35ac9a88bcd477f0c
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Thu Feb 6 20:40:50 2020 -0800

    simplified acquireLock logic
---
 .../org/apache/helix/lock/ZKHelixNonblockingLock.java    | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
index 0573228..03e712f 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
@@ -105,20 +105,8 @@ public class ZKHelixNonblockingLock implements HelixLock {
     }
     lockInfo.setLongField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(), deadline);
 
-    // Try to create the lock node
-    boolean success = _baseDataAccessor.create(_lockPath, lockInfo, AccessOption.PERSISTENT);
-
-    // If fail to create the lock node (acquire the lock), compare the timeout timestamp of current lock node with current time, if already passes the timeout, release current lock and try to acquire the lock again
-    if (!success) {
-      Stat stat = new Stat();
-      ZNRecord curLock = _baseDataAccessor.get(_lockPath, stat, AccessOption.PERSISTENT);
-      if (hasTimedOut(curLock)) {
-        // set may fail when the znode version changes in between the get and set, meaning there are some changes in the lock
-        success =
-            _baseDataAccessor.set(_lockPath, lockInfo, stat.getVersion(), AccessOption.PERSISTENT);
-      }
-    }
-    return success;
+    LockUpdater updater = new LockUpdater(lockInfo);
+    return _baseDataAccessor.update(_lockPath, updater, AccessOption.PERSISTENT);
   }
 
   @Override


[helix] 19/19: Rename interface HelixLock to DistributedLock

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 9b2d799ce1b7d50743b897596ef8c82e6b4c0692
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Tue Feb 18 17:58:19 2020 -0800

    Rename interface HelixLock to DistributedLock
---
 .../lock/{HelixLock.java => DistributedLock.java}  |  2 +-
 .../main/java/org/apache/helix/lock/LockInfo.java  | 27 ++++++++++++++--------
 ...Lock.java => ZKDistributedNonblockingLock.java} | 11 +++++----
 .../lock/helix/TestZKHelixNonblockingLock.java     | 12 +++++-----
 4 files changed, 30 insertions(+), 22 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java b/helix-lock/src/main/java/org/apache/helix/lock/DistributedLock.java
similarity index 98%
rename from helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
rename to helix-lock/src/main/java/org/apache/helix/lock/DistributedLock.java
index 97e2c35..594ccf4 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/DistributedLock.java
@@ -22,7 +22,7 @@ package org.apache.helix.lock;
 /**
  * Generic interface for Helix distributed lock
  */
-public interface HelixLock {
+public interface DistributedLock {
   /**
    * Blocking call to acquire a lock
    * @return true if the lock was successfully acquired,
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
index 2c9a24f..71f7258 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
@@ -36,20 +36,23 @@ public class LockInfo {
   public static final LockInfo defaultLockInfo =
       new LockInfo(DEFAULT_OWNER_TEXT, DEFAULT_MESSAGE_TEXT, DEFAULT_TIMEOUT_LONG);
 
-  private ZNRecord record;
+  private static final String ZNODE_ID = "LOCK";
+  private ZNRecord _record;
 
   /**
    * The keys to lock information
    */
   public enum LockInfoAttribute {
-    OWNER, MESSAGE, TIMEOUT
+    OWNER,
+    MESSAGE,
+    TIMEOUT
   }
 
   /**
    * Initialize a default LockInfo instance
    */
   private LockInfo() {
-    record = new ZNRecord("LOCK");
+    _record = new ZNRecord(ZNODE_ID);
     setLockInfoFields(DEFAULT_OWNER_TEXT, DEFAULT_MESSAGE_TEXT, DEFAULT_TIMEOUT_LONG);
   }
 
@@ -85,11 +88,11 @@ public class LockInfo {
    * @param timeout value of TIMEOUT attribute
    */
   private void setLockInfoFields(String ownerId, String message, long timeout) {
-    record.setSimpleField(LockInfoAttribute.OWNER.name(),
+    _record.setSimpleField(LockInfoAttribute.OWNER.name(),
         ownerId == null ? DEFAULT_OWNER_TEXT : ownerId);
-    record.setSimpleField(LockInfoAttribute.MESSAGE.name(),
+    _record.setSimpleField(LockInfoAttribute.MESSAGE.name(),
         message == null ? DEFAULT_MESSAGE_TEXT : message);
-    record.setLongField(LockInfoAttribute.TIMEOUT.name(), timeout);
+    _record.setLongField(LockInfoAttribute.TIMEOUT.name(), timeout);
   }
 
   /**
@@ -97,7 +100,7 @@ public class LockInfo {
    * @return the owner id of the lock, empty string if there is no owner id set
    */
   public String getOwner() {
-    String owner = record.getSimpleField(LockInfoAttribute.OWNER.name());
+    String owner = _record.getSimpleField(LockInfoAttribute.OWNER.name());
     return owner == null ? DEFAULT_OWNER_TEXT : owner;
   }
 
@@ -106,7 +109,7 @@ public class LockInfo {
    * @return the message of the lock, empty string if there is no message set
    */
   public String getMessage() {
-    String message = record.getSimpleField(LockInfoAttribute.MESSAGE.name());
+    String message = _record.getSimpleField(LockInfoAttribute.MESSAGE.name());
     return message == null ? DEFAULT_MESSAGE_TEXT : message;
   }
 
@@ -115,10 +118,14 @@ public class LockInfo {
    * @return the expiring time of the lock, -1 if there is no timeout set
    */
   public Long getTimeout() {
-    return record.getLongField(LockInfoAttribute.TIMEOUT.name(), DEFAULT_TIMEOUT_LONG);
+    return _record.getLongField(LockInfoAttribute.TIMEOUT.name(), DEFAULT_TIMEOUT_LONG);
   }
 
+  /**
+   * Get the underlying ZNRecord in a LockInfo
+   * @return lock information contained in a ZNRecord
+   */
   public ZNRecord getRecord() {
-    return record;
+    return _record;
   }
 }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKDistributedNonblockingLock.java
similarity index 90%
rename from helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java
rename to helix-lock/src/main/java/org/apache/helix/lock/helix/ZKDistributedNonblockingLock.java
index cbfe9da..8fe8c8c 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKDistributedNonblockingLock.java
@@ -26,7 +26,7 @@ import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.ZNRecord;
-import org.apache.helix.lock.HelixLock;
+import org.apache.helix.lock.DistributedLock;
 import org.apache.helix.lock.LockInfo;
 import org.apache.helix.lock.LockScope;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
@@ -36,9 +36,9 @@ import org.apache.log4j.Logger;
 /**
  * Helix nonblocking lock implementation based on Zookeeper
  */
-public class ZKHelixNonblockingLock implements HelixLock {
+public class ZKDistributedNonblockingLock implements DistributedLock {
 
-  private static final Logger LOG = Logger.getLogger(ZKHelixNonblockingLock.class);
+  private static final Logger LOG = Logger.getLogger(ZKDistributedNonblockingLock.class);
 
   private final String _lockPath;
   private final String _userId;
@@ -54,7 +54,7 @@ public class ZKHelixNonblockingLock implements HelixLock {
    * @param lockMsg the reason for having this lock
    * @param userId a universal unique userId for lock owner identity
    */
-  public ZKHelixNonblockingLock(LockScope scope, String zkAddress, Long timeout, String lockMsg,
+  public ZKDistributedNonblockingLock(LockScope scope, String zkAddress, Long timeout, String lockMsg,
       String userId) {
     this(scope.getPath(), zkAddress, timeout, lockMsg, userId);
   }
@@ -67,7 +67,7 @@ public class ZKHelixNonblockingLock implements HelixLock {
    * @param lockMsg the reason for having this lock
    * @param userId a universal unique userId for lock owner identity
    */
-  private ZKHelixNonblockingLock(String lockPath, String zkAddress, Long timeout, String lockMsg,
+  private ZKDistributedNonblockingLock(String lockPath, String zkAddress, Long timeout, String lockMsg,
       String userId) {
     _lockPath = lockPath;
     if (timeout < 0) {
@@ -94,6 +94,7 @@ public class ZKHelixNonblockingLock implements HelixLock {
     return _baseDataAccessor.update(_lockPath, updater, AccessOption.PERSISTENT);
   }
 
+  //TODO: update release lock logic so it would not leave empty znodes after the lock is released
   @Override
   public boolean releaseLock() {
     // Initialize the lock updater with a default lock info represents the state of a unlocked lock
diff --git a/helix-lock/src/test/java/org/apache/helix/lock/helix/TestZKHelixNonblockingLock.java b/helix-lock/src/test/java/org/apache/helix/lock/helix/TestZKHelixNonblockingLock.java
index 39205af..08cf33b 100644
--- a/helix-lock/src/test/java/org/apache/helix/lock/helix/TestZKHelixNonblockingLock.java
+++ b/helix-lock/src/test/java/org/apache/helix/lock/helix/TestZKHelixNonblockingLock.java
@@ -42,7 +42,7 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
   private final String _clusterName = TestHelper.getTestClassName();
   private final String _lockMessage = "Test";
   private String _lockPath;
-  private ZKHelixNonblockingLock _lock;
+  private ZKDistributedNonblockingLock _lock;
   private String _userId;
   private HelixLockScope _participantScope;
 
@@ -61,7 +61,7 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
 
     _participantScope = new HelixLockScope(HelixLockScope.LockScopeProperty.CLUSTER, pathKeys);
     _lockPath = _participantScope.getPath();
-    _lock = new ZKHelixNonblockingLock(_participantScope, ZK_ADDR, Long.MAX_VALUE, _lockMessage,
+    _lock = new ZKDistributedNonblockingLock(_participantScope, ZK_ADDR, Long.MAX_VALUE, _lockMessage,
         _userId);
   }
 
@@ -137,8 +137,8 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
   public void testSimultaneousAcquire() {
     List<Callable<Boolean>> threads = new ArrayList<>();
     for (int i = 0; i < 2; i++) {
-      ZKHelixNonblockingLock lock =
-          new ZKHelixNonblockingLock(_participantScope, ZK_ADDR, Long.MAX_VALUE, _lockMessage,
+      ZKDistributedNonblockingLock lock =
+          new ZKDistributedNonblockingLock(_participantScope, ZK_ADDR, Long.MAX_VALUE, _lockMessage,
               UUID.randomUUID().toString());
       threads.add(new TestSimultaneousAcquireLock(lock));
     }
@@ -148,9 +148,9 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
   }
 
   private static class TestSimultaneousAcquireLock implements Callable<Boolean> {
-    final ZKHelixNonblockingLock _lock;
+    final ZKDistributedNonblockingLock _lock;
 
-    TestSimultaneousAcquireLock(ZKHelixNonblockingLock lock) {
+    TestSimultaneousAcquireLock(ZKDistributedNonblockingLock lock) {
       _lock = lock;
     }
 


[helix] 14/19: Added cluster level to HelixLockScope and convert lock path to uppercase

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 26abd847d706d254cc1c90777d7242cd45da04c3
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Fri Feb 7 14:53:03 2020 -0800

    Added cluster level to HelixLockScope and convert lock path to uppercase
---
 .../java/org/apache/helix/lock/HelixLockScope.java | 39 ++++++++++++++++------
 .../apache/helix/lock/ZKHelixNonblockingLock.java  | 12 +------
 .../helix/lock/TestZKHelixNonblockingLock.java     |  6 ++--
 3 files changed, 32 insertions(+), 25 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java
index 9fc12fc..e31991a 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java
@@ -34,11 +34,13 @@ public class HelixLockScope {
    */
   public enum LockScopeProperty {
 
-    PARTICIPANT(1, 0),
+    CLUSTER(1, 0),
 
-    RESOURCE(2, 1),
+    PARTICIPANT(2, 1),
 
-    PARTITION(3, 2);
+    RESOURCE(3, 2),
+
+    PARTITION(4, 3);
 
     final int _zkPathArgNum;
     final int _argumentPos;
@@ -71,14 +73,17 @@ public class HelixLockScope {
   private static final StringTemplate template = new StringTemplate();
 
   static {
-    template.addEntry(HelixLockScope.LockScopeProperty.PARTICIPANT, 1, "/{participantName}");
-    template.addEntry(HelixLockScope.LockScopeProperty.RESOURCE, 2,
-        "/{participantName}/{resourceName}");
-    template.addEntry(HelixLockScope.LockScopeProperty.PARTITION, 3,
-        "/{participantName}/{resourceName}/{partitionName}");
+    template.addEntry(LockScopeProperty.CLUSTER, 1, "/{clusterName}");
+    template.addEntry(HelixLockScope.LockScopeProperty.PARTICIPANT, 2,
+        "/{clusterName}/{participantName}");
+    template.addEntry(HelixLockScope.LockScopeProperty.RESOURCE, 3,
+        "/{clusterName}/{participantName}/{resourceName}");
+    template.addEntry(HelixLockScope.LockScopeProperty.PARTITION, 4,
+        "/{clusterName}/{participantName}/{resourceName}/{partitionName}");
   }
 
   private final HelixLockScope.LockScopeProperty _type;
+  private final String _clusterName;
   private final String _participantName;
   private final String _resourceName;
   private final String _partitionName;
@@ -101,7 +106,13 @@ public class HelixLockScope {
     _type = type;
 
     //Initialize the name fields for various scope
-    _participantName = zkPathKeys.get(LockScopeProperty.PARTICIPANT.getArgumentPos());
+    _clusterName = zkPathKeys.get(LockScopeProperty.CLUSTER.getArgumentPos());
+
+    if (type.getZkPathArgNum() >= LockScopeProperty.PARTICIPANT.getZkPathArgNum()) {
+      _participantName = zkPathKeys.get(LockScopeProperty.PARTICIPANT.getArgumentPos());
+    } else {
+      _participantName = null;
+    }
 
     if (type.getZkPathArgNum() >= LockScopeProperty.RESOURCE.getZkPathArgNum()) {
       _resourceName = zkPathKeys.get(LockScopeProperty.RESOURCE.getArgumentPos());
@@ -115,7 +126,7 @@ public class HelixLockScope {
       _partitionName = null;
     }
 
-    _zkPath = template.instantiate(type, zkPathKeys.toArray(new String[0]));
+    _zkPath = template.instantiate(type, zkPathKeys.toArray(new String[0])).toUpperCase();
   }
 
   /**
@@ -127,6 +138,14 @@ public class HelixLockScope {
   }
 
   /**
+   * Get the cluster name if it exists
+   * @return the cluster name
+   */
+  public String getClusterName() {
+    return _clusterName;
+  }
+
+  /**
    * Get the participant name if it exists
    * @return the participant name
    */
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
index 03e712f..18cac1a 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
@@ -20,24 +20,14 @@
 package org.apache.helix.lock;
 
 import java.util.Date;
-import java.util.UUID;
 
-import com.google.common.annotations.VisibleForTesting;
 import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.HelixException;
-import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.PropertyType;
 import org.apache.helix.ZNRecord;
-import org.apache.helix.api.exceptions.HelixMetaDataAccessException;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.model.HelixConfigScope;
 import org.apache.log4j.Logger;
-import org.apache.zookeeper.data.Stat;
 
 import static org.apache.helix.lock.ZKHelixNonblockingLockInfo.DEFAULT_OWNER_TEXT;
 import static org.apache.helix.lock.ZKHelixNonblockingLockInfo.DEFAULT_TIMEOUT_LONG;
@@ -68,7 +58,7 @@ public class ZKHelixNonblockingLock implements HelixLock {
    */
   public ZKHelixNonblockingLock(String clusterName, HelixLockScope scope, String zkAddress,
       Long timeout, String lockMsg, String userId) {
-    this("/" + clusterName + LOCK_ROOT + scope.getZkPath(), zkAddress, timeout, lockMsg, userId);
+    this("/" + clusterName.toUpperCase() + LOCK_ROOT + scope.getZkPath(), zkAddress, timeout, lockMsg, userId);
   }
 
   /**
diff --git a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
index 96a15b3..090a43d 100644
--- a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
+++ b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
@@ -26,11 +26,8 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 
-import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.PropertyType;
 import org.apache.helix.TestHelper;
 import org.apache.helix.ZNRecord;
-import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.zookeeper.CreateMode;
 import org.testng.Assert;
@@ -59,12 +56,13 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
     _userId = UUID.randomUUID().toString();
 
     List<String> pathKeys = new ArrayList<>();
+    pathKeys.add(_clusterName);
     pathKeys.add("participant_name");
     pathKeys.add("resource_name");
     pathKeys.add("partition_name");
 
     _participantScope = new HelixLockScope(HelixLockScope.LockScopeProperty.PARTITION, pathKeys);
-    _lockPath = "/" + _clusterName + _lockRoot + _participantScope.getZkPath();
+    _lockPath = "/" + _clusterName.toUpperCase() + _lockRoot + _participantScope.getZkPath();
     _lock = new ZKHelixNonblockingLock(_clusterName, _participantScope, ZK_ADDR, Long.MAX_VALUE,
         _lockMessage, _userId);
   }


[helix] 11/19: Changed method doc for releaseLock in HelixLock interface

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 05d05ce020153c4e3c97598449939b8cbcc310c1
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Thu Feb 6 12:02:00 2020 -0800

    Changed method doc for releaseLock in HelixLock interface
---
 helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
index 2d7e318..ec425f6 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
@@ -32,9 +32,8 @@ public interface HelixLock {
 
   /**
    * Blocking call to release a lock
-   * @return true if the lock was successfully released,
-   * false if the locked is not locked or is not locked by the user,
-   * or the lock could not be released
+   * @return true if the lock was successfully released or if the locked is not currently locked,
+   * false if the lock is not locked by the user or the release operation failed
    */
   boolean releaseLock();
 


[helix] 02/19: Created Helix distributed lock interface (#703)

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 6f4a5bda87c8b159b520e29006e84f779b59de0d
Author: mgao0 <31...@users.noreply.github.com>
AuthorDate: Thu Jan 30 13:39:16 2020 -0800

    Created Helix distributed lock interface (#703)
    
    Added LockInfo interfaces.
---
 .../main/java/org/apache/helix/lock/HelixLock.java | 53 ++++++++++++++++++++++
 .../main/java/org/apache/helix/lock/LockInfo.java  | 46 +++++++++++++++++++
 2 files changed, 99 insertions(+)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
new file mode 100644
index 0000000..01ef63b
--- /dev/null
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
@@ -0,0 +1,53 @@
+package org.apache.helix.lock;
+
+/*
+ * 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.
+ */
+
+/**
+ * Generic interface for Helix distributed lock
+ */
+public interface HelixLock<T> {
+  /**
+   * Blocking call to acquire a lock
+   * @return true if the lock was successfully acquired,
+   * false if the lock could not be acquired
+   */
+  boolean acquireLock();
+
+  /**
+   * Blocking call to release a lock
+   * @return true if the lock was successfully released,
+   * false if the locked is not locked or is not locked by the user,
+   * or the lock could not be released
+   */
+  boolean releaseLock();
+
+  /**
+   * Retrieve the lock information, e.g. lock timeout, lock message, etc.
+   * @return lock metadata information
+   */
+  LockInfo<T> getLockInfo();
+
+  /**
+   * If the user is current lock owner
+   * @return true if the user is the lock owner,
+   * false if the user is not the lock owner or the lock doesn't have a owner
+   */
+  boolean isOwner();
+}
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
new file mode 100644
index 0000000..30322bb
--- /dev/null
+++ b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package org.apache.helix.lock;
+
+import java.util.Map;
+
+
+/**
+ * Generic interface for a map contains the Helix lock information
+ * @param <T> The type of the LockInfo value
+ */
+public interface LockInfo<T> {
+
+  //TODO: add specific setter and getter for any field that is determined to be universal for all implementations of HelixLock
+
+  /**
+   * Create a single filed of LockInfo, or update the value of the field if it already exists
+   * @param infoKey the key of the LockInfo field
+   * @param infoValue the value of the LockInfo field
+   */
+  void setInfoValue(String infoKey, T infoValue);
+
+  /**
+   * Get the value of a field in LockInfo
+   * @param infoKey the key of the LockInfo field
+   * @return the value of the field or null if this key does not exist
+   */
+  T getInfoValue(String infoKey);
+}


[helix] 04/19: Added LockInfo interface

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit b243b6dddcd09aa6b254cfc7cfcc4002b3dd5397
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Fri Jan 24 15:33:04 2020 -0800

    Added LockInfo interface
---
 helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
index a04ac01..d163718 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
@@ -20,9 +20,8 @@ package org.apache.helix.lock;
  */
 
 /**
- * Generic interface for Helix distributed lock
+ * Generic interface for Helix distributed lock for both nonblocking and blocking calls
  */
-<<<<<<< HEAD
 public interface HelixLock<T> {
   /**
    * Blocking call to acquire a lock


[helix] 10/19: A few fixes on syntax

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 0dea882256bf1f5d63aabb11ec334aec4503a8da
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Wed Feb 5 18:07:00 2020 -0800

    A few fixes on syntax
---
 .../apache/helix/lock/ZKHelixNonblockingLock.java  | 94 +++++++++++++---------
 .../helix/lock/ZKHelixNonblockingLockInfo.java     | 38 ++++++++-
 .../helix/lock/TestZKHelixNonblockingLock.java     |  3 +-
 3 files changed, 92 insertions(+), 43 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
index 57ff9aa..0424177 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
@@ -19,14 +19,17 @@
 
 package org.apache.helix.lock;
 
+import java.util.Date;
 import java.util.UUID;
 
 import com.google.common.annotations.VisibleForTesting;
 import org.I0Itec.zkclient.DataUpdater;
+import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.ZNRecord;
+import org.apache.helix.api.exceptions.HelixMetaDataAccessException;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.manager.zk.ZkClient;
 import org.apache.helix.manager.zk.client.HelixZkClient;
@@ -34,6 +37,9 @@ import org.apache.helix.model.HelixConfigScope;
 import org.apache.log4j.Logger;
 import org.apache.zookeeper.data.Stat;
 
+import static org.apache.helix.lock.ZKHelixNonblockingLockInfo.DEFAULT_OWNER_TEXT;
+import static org.apache.helix.lock.ZKHelixNonblockingLockInfo.DEFAULT_TIMEOUT_LONG;
+
 
 /**
  * Helix nonblocking lock implementation based on Zookeeper
@@ -44,8 +50,6 @@ public class ZKHelixNonblockingLock implements HelixLock {
 
   private static final String LOCK_ROOT = "LOCKS";
   private static final String PATH_DELIMITER = "/";
-  private static final String UUID_FORMAT_REGEX =
-      "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
   private final String _lockPath;
   private final String _userId;
   private final long _timeout;
@@ -63,8 +67,8 @@ public class ZKHelixNonblockingLock implements HelixLock {
    */
   public ZKHelixNonblockingLock(String clusterName, HelixConfigScope scope, String zkAddress,
       Long timeout, String lockMsg, String userId) {
-    this(PATH_DELIMITER + clusterName + PATH_DELIMITER + LOCK_ROOT + PATH_DELIMITER + scope,
-        zkAddress, timeout, lockMsg, userId);
+    this(PATH_DELIMITER +  String.join(PATH_DELIMITER, clusterName, LOCK_ROOT, scope.getZkPath()), zkAddress, timeout, lockMsg,
+        userId);
   }
 
   /**
@@ -77,15 +81,11 @@ public class ZKHelixNonblockingLock implements HelixLock {
    */
   private ZKHelixNonblockingLock(String lockPath, String zkAddress, Long timeout, String lockMsg,
       String userId) {
-    HelixZkClient zkClient = new ZkClient(zkAddress);
     _lockPath = lockPath;
     _timeout = timeout;
     _lockMsg = lockMsg;
-    if (!userId.matches(UUID_FORMAT_REGEX)) {
-      throw new IllegalArgumentException("The input user id is not a valid UUID.");
-    }
     _userId = userId;
-    _baseDataAccessor = new ZkBaseDataAccessor<ZNRecord>(zkClient.getServers());
+    _baseDataAccessor = new ZkBaseDataAccessor<>(zkAddress);
   }
 
   @Override
@@ -95,16 +95,15 @@ public class ZKHelixNonblockingLock implements HelixLock {
     ZNRecord lockInfo = new ZNRecord(_userId);
     lockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name(), _userId);
     lockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE.name(), _lockMsg);
-    long timeout;
 
-    // If the input timeout value is the max value, set the expire time to max value
-    if (_timeout == Long.MAX_VALUE) {
-      timeout = _timeout;
+    long deadline;
+    // Prevent value overflow
+    if (_timeout > Long.MAX_VALUE - System.currentTimeMillis()) {
+      deadline = Long.MAX_VALUE;
     } else {
-      timeout = System.currentTimeMillis() + _timeout;
+      deadline = System.currentTimeMillis() + _timeout;
     }
-    lockInfo
-        .setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(), String.valueOf(timeout));
+    lockInfo.setLongField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(), deadline);
 
     // Try to create the lock node
     boolean success = _baseDataAccessor.create(_lockPath, lockInfo, AccessOption.PERSISTENT);
@@ -113,9 +112,7 @@ public class ZKHelixNonblockingLock implements HelixLock {
     if (!success) {
       Stat stat = new Stat();
       ZNRecord curLock = _baseDataAccessor.get(_lockPath, stat, AccessOption.PERSISTENT);
-      long curTimeout =
-          Long.parseLong(curLock.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name()));
-      if (System.currentTimeMillis() >= curTimeout) {
+      if (hasTimedOut(curLock)) {
         // set may fail when the znode version changes in between the get and set, meaning there are some changes in the lock
         success =
             _baseDataAccessor.set(_lockPath, lockInfo, stat.getVersion(), AccessOption.PERSISTENT);
@@ -126,22 +123,13 @@ public class ZKHelixNonblockingLock implements HelixLock {
 
   @Override
   public boolean releaseLock() {
-    ZNRecord newLockInfo = new ZNRecord(_userId);
-    newLockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name(),
-        ZKHelixNonblockingLockInfo.DEFAULT_OWNER_TEXT);
-    newLockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE.name(),
-        ZKHelixNonblockingLockInfo.DEFAULT_MESSAGE_TEXT);
-    newLockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(),
-        ZKHelixNonblockingLockInfo.DEFAULT_TIMEOUT_TEXT);
+    ZNRecord newLockInfo = new ZKHelixNonblockingLockInfo<>().toZNRecord();
     LockUpdater updater = new LockUpdater(newLockInfo);
     return _baseDataAccessor.update(_lockPath, updater, AccessOption.PERSISTENT);
   }
 
   @Override
   public ZKHelixNonblockingLockInfo getLockInfo() {
-    if (!_baseDataAccessor.exists(_lockPath, AccessOption.PERSISTENT)) {
-      return new ZKHelixNonblockingLockInfo();
-    }
     ZNRecord curLockInfo = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
     return new ZKHelixNonblockingLockInfo(curLockInfo);
   }
@@ -149,9 +137,6 @@ public class ZKHelixNonblockingLock implements HelixLock {
   @Override
   public boolean isOwner() {
     ZNRecord curLockInfo = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
-    if (curLockInfo == null) {
-      return false;
-    }
     return userIdMatches(curLockInfo) && !hasTimedOut(curLockInfo);
   }
 
@@ -161,8 +146,12 @@ public class ZKHelixNonblockingLock implements HelixLock {
    * @return return true if the lock has timed out, otherwise return false.
    */
   private boolean hasTimedOut(ZNRecord record) {
-    String timeoutStr = record.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name());
-    return System.currentTimeMillis() >= Long.parseLong(timeoutStr);
+    if (record == null) {
+      return false;
+    }
+    long timeout = record
+        .getLongField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(), DEFAULT_TIMEOUT_LONG);
+    return System.currentTimeMillis() >= timeout;
   }
 
   /**
@@ -171,18 +160,43 @@ public class ZKHelixNonblockingLock implements HelixLock {
    * @return return true if the two ids match, otherwise return false.
    */
   private boolean userIdMatches(ZNRecord record) {
+    if (record == null) {
+      return false;
+    }
     String ownerId = record.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name());
     return ownerId.equals(_userId);
   }
 
   /**
-   * Class that specifies how a lock node should be updated with another lock node for a lock owner only
+   * Check if the lock node has a owner id field in the lock information
+   * @param record Lock information in format of ZNRecord
+   * @return true if the lock has a owner id field that the ownership can be or not be timed out, otherwise false
+   */
+  private boolean hasOwnerField(ZNRecord record) {
+    if (record == null) {
+      return false;
+    }
+    String ownerId = record.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name());
+    return ownerId != null && !ownerId.equals(DEFAULT_OWNER_TEXT);
+  }
+
+  /**
+   * Check if the lock node has a current owner
+   * @param record Lock information in format of ZNRecord
+   * @return true if the lock has a current owner that the ownership has not be timed out, otherwise false
+   */
+  private boolean hasNonExpiredOwner(ZNRecord record) {
+    return hasOwnerField(record) && !hasTimedOut(record);
+  }
+
+  /**
+   * Class that specifies how a lock node should be updated with another lock node
    */
   private class LockUpdater implements DataUpdater<ZNRecord> {
     final ZNRecord _record;
 
     /**
-     * Initialize a structure for lock owner to update a lock node value
+     * Initialize a structure for lock user to update a lock node value
      * @param record the lock node value will be updated in ZNRecord format
      */
     public LockUpdater(ZNRecord record) {
@@ -191,9 +205,15 @@ public class ZKHelixNonblockingLock implements HelixLock {
 
     @Override
     public ZNRecord update(ZNRecord current) {
-      if (current != null && userIdMatches(current) && !hasTimedOut(current)) {
+      // If no one owns the lock, allow the update
+      // If the lock owner id matches user id, allow the update
+      if (!hasNonExpiredOwner(current) || userIdMatches(current)) {
         return _record;
       }
+      // For users who are not the lock owner and try to do an update on a lock that is held by someone else, exception thrown is to be caught by data accessor, and return false for the update
+      LOG.error(
+          "User " + _userId + "tried to update the lock at " + new Date(System.currentTimeMillis())
+              + ". Lock path: " + _lockPath);
       throw new HelixException("User is not authorized to perform this operation.");
     }
   }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
index 50daa03..6e62717 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
@@ -29,13 +29,17 @@ public class ZKHelixNonblockingLockInfo<K extends ZKHelixNonblockingLockInfo.Inf
 
   public static final String DEFAULT_OWNER_TEXT = "";
   public static final String DEFAULT_MESSAGE_TEXT = "";
-  public static final String DEFAULT_TIMEOUT_TEXT = String.valueOf(-1);
+  public static final long DEFAULT_TIMEOUT_LONG = -1L;
+  public static final String DEFAULT_TIMEOUT_TEXT = String.valueOf(DEFAULT_TIMEOUT_LONG);
   private Map<InfoKey, String> lockInfo;
 
   public enum InfoKey {
     OWNER, MESSAGE, TIMEOUT
   }
 
+  /**
+   * Constructor of ZKHelixNonblockingLockInfo that set each field to default data
+   */
   public ZKHelixNonblockingLockInfo() {
     lockInfo = new HashMap<>();
     lockInfo.put(InfoKey.OWNER, DEFAULT_OWNER_TEXT);
@@ -43,11 +47,22 @@ public class ZKHelixNonblockingLockInfo<K extends ZKHelixNonblockingLockInfo.Inf
     lockInfo.put(InfoKey.TIMEOUT, DEFAULT_TIMEOUT_TEXT);
   }
 
+  /**
+   * Construct a ZKHelixNonblockingLockInfo using a ZNRecord format of data
+   * @param znRecord A ZNRecord that contains lock information in its simple fields
+   */
   public ZKHelixNonblockingLockInfo(ZNRecord znRecord) {
     this();
-    lockInfo.put(InfoKey.OWNER, znRecord.getSimpleField(InfoKey.OWNER.name()));
-    lockInfo.put(InfoKey.MESSAGE, znRecord.getSimpleField(InfoKey.MESSAGE.name()));
-    lockInfo.put(InfoKey.TIMEOUT, znRecord.getSimpleField(InfoKey.TIMEOUT.name()));
+    if (znRecord == null) {
+      return;
+    }
+    Map<String, String> simpleFields = znRecord.getSimpleFields();
+    lockInfo
+        .put(InfoKey.OWNER, simpleFields.getOrDefault(InfoKey.OWNER.name(), DEFAULT_OWNER_TEXT));
+    lockInfo.put(InfoKey.MESSAGE,
+        simpleFields.getOrDefault(InfoKey.MESSAGE.name(), DEFAULT_MESSAGE_TEXT));
+    lockInfo.put(InfoKey.TIMEOUT,
+        simpleFields.getOrDefault(InfoKey.TIMEOUT.name(), DEFAULT_TIMEOUT_TEXT));
   }
 
   @Override
@@ -59,4 +74,19 @@ public class ZKHelixNonblockingLockInfo<K extends ZKHelixNonblockingLockInfo.Inf
   public String getInfoValue(InfoKey infoKey) {
     return lockInfo.get(infoKey);
   }
+
+  /**
+   * Method to convert a ZKHelixNonblockingLockInfo to ZNRecord
+   * @return a ZNRecord format data contains lock information in its simple fields
+   */
+  public ZNRecord toZNRecord() {
+    ZNRecord znRecord = new ZNRecord("");
+    znRecord.setSimpleField(InfoKey.OWNER.name(),
+        lockInfo.getOrDefault(InfoKey.OWNER, DEFAULT_OWNER_TEXT));
+    znRecord.setSimpleField(InfoKey.MESSAGE.name(),
+        lockInfo.getOrDefault(InfoKey.MESSAGE, DEFAULT_MESSAGE_TEXT));
+    znRecord.setSimpleField(InfoKey.TIMEOUT.name(),
+        lockInfo.getOrDefault(InfoKey.TIMEOUT, DEFAULT_TIMEOUT_TEXT));
+    return znRecord;
+  }
 }
diff --git a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
index f2308c1..29f84ae 100644
--- a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
+++ b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
@@ -66,8 +66,7 @@ public class TestZKHelixNonblockingLock extends ZkUnitTestBase {
     _participantScope =
         new HelixConfigScopeBuilder(ConfigScopeProperty.PARTICIPANT).forCluster(_clusterName)
             .forParticipant("localhost_12918").build();
-
-    _lockPath = "/" + _clusterName + '/' + "LOCKS" + '/' + _participantScope;
+    _lockPath = "/" + _clusterName + '/' + "LOCKS" + '/' + _participantScope.getZkPath();
     _lock = new ZKHelixNonblockingLock(_clusterName, _participantScope, ZK_ADDR, Long.MAX_VALUE,
         _lockMessage, _userId);
   }


[helix] 16/19: Created LockScope interface

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit c899db59d35df634c4888906ee1f999fbbc16b6b
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Fri Feb 14 13:28:07 2020 -0800

    Created LockScope interface
---
 .../main/java/org/apache/helix/lock/LockInfo.java  | 15 +++++--
 .../main/java/org/apache/helix/lock/LockScope.java | 25 ++++++++++++
 .../helix/lock/{ => helix}/HelixLockScope.java     | 47 +++++++++++-----------
 .../lock/{ => helix}/ZKHelixNonblockingLock.java   | 19 ++++-----
 .../{ => helix}/TestZKHelixNonblockingLock.java    |  5 ++-
 5 files changed, 72 insertions(+), 39 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
index 3829d40..212c93c 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
@@ -138,7 +138,7 @@ public class LockInfo extends HelixProperty {
    * Get the value for OWNER attribute of the lock from a ZNRecord
    * @return the owner id of the lock, empty string if there is no owner id set
    */
-  static String getOwner(ZNRecord znRecord) {
+  public static String getOwner(ZNRecord znRecord) {
     if (znRecord == null) {
       return DEFAULT_OWNER_TEXT;
     }
@@ -150,7 +150,7 @@ public class LockInfo extends HelixProperty {
    * Get the value for MESSAGE attribute of the lock from a ZNRecord
    * @return the message of the lock, empty string if there is no message set
    */
-  static String getMessage(ZNRecord znRecord) {
+  public static String getMessage(ZNRecord znRecord) {
     if (znRecord == null) {
       return DEFAULT_MESSAGE_TEXT;
     }
@@ -162,10 +162,19 @@ public class LockInfo extends HelixProperty {
    * Get the value for TIMEOUT attribute of the lock from a ZNRecord
    * @return the expiring time of the lock, -1 if there is no timeout set
    */
-  static long getTimeout(ZNRecord znRecord) {
+  public static long getTimeout(ZNRecord znRecord) {
     if (znRecord == null) {
       return DEFAULT_TIMEOUT_LONG;
     }
     return znRecord.getLongField(LockInfoAttribute.TIMEOUT.name(), DEFAULT_TIMEOUT_LONG);
   }
+
+  /**
+   * Check if the lock has a owner id set
+   * @return true if an owner id is set, false if not
+   */
+  public static boolean ownerIdSet(ZNRecord znRecord) {
+    String ownerId = getOwner(znRecord);
+    return !ownerId.equals(DEFAULT_OWNER_TEXT);
+  }
 }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/LockScope.java b/helix-lock/src/main/java/org/apache/helix/lock/LockScope.java
new file mode 100644
index 0000000..ff2d329
--- /dev/null
+++ b/helix-lock/src/main/java/org/apache/helix/lock/LockScope.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package org.apache.helix.lock;
+
+public interface LockScope {
+
+  String getPath();
+}
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java b/helix-lock/src/main/java/org/apache/helix/lock/helix/HelixLockScope.java
similarity index 80%
rename from helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java
rename to helix-lock/src/main/java/org/apache/helix/lock/helix/HelixLockScope.java
index 3df0e7e..77907ea 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/helix/HelixLockScope.java
@@ -17,17 +17,18 @@
  * under the License.
  */
 
-package org.apache.helix.lock;
+package org.apache.helix.lock.helix;
 
 import java.util.List;
 
+import org.apache.helix.lock.LockScope;
 import org.apache.helix.util.StringTemplate;
 
 
 /**
  *  Defines the various scopes of Helix locks, and how they are represented on Zookeeper
  */
-public class HelixLockScope {
+public class HelixLockScope implements LockScope {
 
   /**
    * Define various properties of Helix lock, and associate them with the number of arguments required for getting znode path
@@ -43,30 +44,30 @@ public class HelixLockScope {
     PARTITION(4);
 
     //the number of arguments required to generate a full path for the specific scope
-    final int _zkPathArgNum;
+    final int _pathArgNum;
 
     /**
      * Initialize a LockScopeProperty
-     * @param zkPathArgNum the number of arguments required to generate a full path for the specific scope
-\     */
-    private LockScopeProperty(int zkPathArgNum) {
-      _zkPathArgNum = zkPathArgNum;
+     * @param pathArgNum the number of arguments required to generate a full path for the specific scope
+    \     */
+    private LockScopeProperty(int pathArgNum) {
+      _pathArgNum = pathArgNum;
     }
 
     /**
      * Get the number of template arguments required to generate a full path
      * @return number of template arguments in the path
      */
-    public int getZkPathArgNum() {
-      return _zkPathArgNum;
+    public int getPathArgNum() {
+      return _pathArgNum;
     }
 
     /**
      * Get the position of this argument from the input that used to generate the scope
      * @return the number of position of value for this property in the list of keys input
      */
-    public int getArgumentPos() {
-      return _zkPathArgNum - 1;
+    public int getArgPos() {
+      return _pathArgNum - 1;
     }
   }
 
@@ -98,33 +99,33 @@ public class HelixLockScope {
    * @param type the scope
    * @param zkPathKeys keys identifying a ZNode location
    */
-  public HelixLockScope(HelixLockScope.LockScopeProperty type, List<String> zkPathKeys) {
+  private HelixLockScope(HelixLockScope.LockScopeProperty type, List<String> zkPathKeys) {
 
-    if (zkPathKeys.size() != type.getZkPathArgNum()) {
+    if (zkPathKeys.size() != type.getPathArgNum()) {
       throw new IllegalArgumentException(
-          type + " requires " + type.getZkPathArgNum() + " arguments to get znode, but was: "
+          type + " requires " + type.getPathArgNum() + " arguments to get znode, but was: "
               + zkPathKeys);
     }
 
     _type = type;
 
     //Initialize the name fields for various scope
-    _clusterName = zkPathKeys.get(LockScopeProperty.CLUSTER.getArgumentPos());
+    _clusterName = zkPathKeys.get(LockScopeProperty.CLUSTER.getArgPos());
 
-    if (type.getZkPathArgNum() >= LockScopeProperty.PARTICIPANT.getZkPathArgNum()) {
-      _participantName = zkPathKeys.get(LockScopeProperty.PARTICIPANT.getArgumentPos());
+    if (type.getPathArgNum() >= LockScopeProperty.PARTICIPANT.getPathArgNum()) {
+      _participantName = zkPathKeys.get(LockScopeProperty.PARTICIPANT.getArgPos());
     } else {
       _participantName = null;
     }
 
-    if (type.getZkPathArgNum() >= LockScopeProperty.RESOURCE.getZkPathArgNum()) {
-      _resourceName = zkPathKeys.get(LockScopeProperty.RESOURCE.getArgumentPos());
+    if (type.getPathArgNum() >= LockScopeProperty.RESOURCE.getPathArgNum()) {
+      _resourceName = zkPathKeys.get(LockScopeProperty.RESOURCE.getArgPos());
     } else {
       _resourceName = null;
     }
 
-    if (type.getZkPathArgNum() >= LockScopeProperty.PARTITION.getZkPathArgNum()) {
-      _partitionName = zkPathKeys.get(LockScopeProperty.PARTITION.getArgumentPos());
+    if (type.getPathArgNum() >= LockScopeProperty.PARTITION.getPathArgNum()) {
+      _partitionName = zkPathKeys.get(LockScopeProperty.PARTITION.getArgPos());
     } else {
       _partitionName = null;
     }
@@ -172,11 +173,11 @@ public class HelixLockScope {
     return _partitionName;
   }
 
+  @Override
   /**
    * Get the path to the corresponding ZNode
    * @return a Zookeeper path
-   */
-  public String getZkPath() {
+   */ public String getPath() {
     return _zkPath;
   }
 }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java
similarity index 91%
rename from helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
rename to helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java
index b0cc8f1..478be95 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java
@@ -17,23 +17,21 @@
  * under the License.
  */
 
-package org.apache.helix.lock;
+package org.apache.helix.lock.helix;
 
 import java.util.Date;
-import java.util.concurrent.locks.Lock;
 
 import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.ZNRecord;
+import org.apache.helix.lock.HelixLock;
+import org.apache.helix.lock.LockInfo;
+import org.apache.helix.lock.LockScope;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.log4j.Logger;
 
-import static org.apache.helix.lock.LockInfo.DEFAULT_OWNER_TEXT;
-import static org.apache.helix.lock.LockInfo.DEFAULT_TIMEOUT_LONG;
-import static org.apache.helix.lock.LockInfo.defaultLockInfo;
-
 
 /**
  * Helix nonblocking lock implementation based on Zookeeper
@@ -56,9 +54,9 @@ public class ZKHelixNonblockingLock implements HelixLock {
    * @param lockMsg the reason for having this lock
    * @param userId a universal unique userId for lock owner identity
    */
-  public ZKHelixNonblockingLock(HelixLockScope scope, String zkAddress, Long timeout,
-      String lockMsg, String userId) {
-    this(scope.getZkPath(), zkAddress, timeout, lockMsg, userId);
+  public ZKHelixNonblockingLock(LockScope scope, String zkAddress, Long timeout, String lockMsg,
+      String userId) {
+    this(scope.getPath(), zkAddress, timeout, lockMsg, userId);
   }
 
   /**
@@ -154,8 +152,7 @@ public class ZKHelixNonblockingLock implements HelixLock {
    * @return true if the lock has a current owner that the ownership has not be timed out, otherwise false
    */
   private boolean hasNonExpiredOwner(ZNRecord znRecord) {
-    String owner = LockInfo.getOwner(znRecord);
-    return !owner.equals(DEFAULT_OWNER_TEXT) && !hasTimedOut(znRecord);
+    return LockInfo.ownerIdSet(znRecord) && !hasTimedOut(znRecord);
   }
 
   /**
diff --git a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java b/helix-lock/src/test/java/org/apache/helix/lock/helix/TestZKHelixNonblockingLock.java
similarity index 97%
rename from helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
rename to helix-lock/src/test/java/org/apache/helix/lock/helix/TestZKHelixNonblockingLock.java
index 5e28688..e79922d 100644
--- a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
+++ b/helix-lock/src/test/java/org/apache/helix/lock/helix/TestZKHelixNonblockingLock.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.helix.lock;
+package org.apache.helix.lock.helix;
 
 import java.util.ArrayList;
 import java.util.Date;
@@ -29,6 +29,7 @@ import java.util.concurrent.Callable;
 import org.apache.helix.TestHelper;
 import org.apache.helix.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
+import org.apache.helix.lock.LockInfo;
 import org.apache.zookeeper.CreateMode;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
@@ -61,7 +62,7 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
     pathKeys.add("partition_name");
 
     _participantScope = new HelixLockScope(HelixLockScope.LockScopeProperty.PARTITION, pathKeys);
-    _lockPath = _participantScope.getZkPath();
+    _lockPath = _participantScope.getPath();
     _lock = new ZKHelixNonblockingLock(_participantScope, ZK_ADDR, Long.MAX_VALUE,
         _lockMessage, _userId);
   }


[helix] 15/19: refactor LockInfo and some updates on the HelixLockScope

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 80877bfb6e8f6dda2af63bce2fed07d3a4982d4d
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Mon Feb 10 18:31:26 2020 -0800

    refactor LockInfo and some updates on the HelixLockScope
---
 .../main/java/org/apache/helix/lock/HelixLock.java |   2 +-
 .../java/org/apache/helix/lock/HelixLockScope.java |  27 ++--
 .../main/java/org/apache/helix/lock/LockInfo.java  | 152 +++++++++++++++++++--
 .../apache/helix/lock/ZKHelixNonblockingLock.java  |  87 ++++++------
 .../helix/lock/ZKHelixNonblockingLockInfo.java     |  92 -------------
 .../helix/lock/TestZKHelixNonblockingLock.java     |  21 ++-
 6 files changed, 203 insertions(+), 178 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
index ec425f6..1cd9e60 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
@@ -41,7 +41,7 @@ public interface HelixLock {
    * Retrieve the lock information, e.g. lock timeout, lock message, etc.
    * @return lock metadata information
    */
-  <K, V> LockInfo<K, V> getLockInfo();
+  LockInfo getLockInfo();
 
   /**
    * If the user is current lock owner
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java
index e31991a..3df0e7e 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java
@@ -34,20 +34,23 @@ public class HelixLockScope {
    */
   public enum LockScopeProperty {
 
-    CLUSTER(1, 0),
+    CLUSTER(1),
 
-    PARTICIPANT(2, 1),
+    PARTICIPANT(2),
 
-    RESOURCE(3, 2),
+    RESOURCE(3),
 
-    PARTITION(4, 3);
+    PARTITION(4);
 
+    //the number of arguments required to generate a full path for the specific scope
     final int _zkPathArgNum;
-    final int _argumentPos;
 
-    private LockScopeProperty(int zkPathArgNum, int argumentPos) {
+    /**
+     * Initialize a LockScopeProperty
+     * @param zkPathArgNum the number of arguments required to generate a full path for the specific scope
+\     */
+    private LockScopeProperty(int zkPathArgNum) {
       _zkPathArgNum = zkPathArgNum;
-      _argumentPos = argumentPos;
     }
 
     /**
@@ -63,7 +66,7 @@ public class HelixLockScope {
      * @return the number of position of value for this property in the list of keys input
      */
     public int getArgumentPos() {
-      return _argumentPos;
+      return _zkPathArgNum - 1;
     }
   }
 
@@ -73,13 +76,13 @@ public class HelixLockScope {
   private static final StringTemplate template = new StringTemplate();
 
   static {
-    template.addEntry(LockScopeProperty.CLUSTER, 1, "/{clusterName}");
+    template.addEntry(LockScopeProperty.CLUSTER, 1, "/{clusterName}/LOCK");
     template.addEntry(HelixLockScope.LockScopeProperty.PARTICIPANT, 2,
-        "/{clusterName}/{participantName}");
+        "/{clusterName}/LOCK/{participantName}");
     template.addEntry(HelixLockScope.LockScopeProperty.RESOURCE, 3,
-        "/{clusterName}/{participantName}/{resourceName}");
+        "/{clusterName}/LOCK/{participantName}/{resourceName}");
     template.addEntry(HelixLockScope.LockScopeProperty.PARTITION, 4,
-        "/{clusterName}/{participantName}/{resourceName}/{partitionName}");
+        "/{clusterName}/LOCK/{participantName}/{resourceName}/{partitionName}");
   }
 
   private final HelixLockScope.LockScopeProperty _type;
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
index afd4c00..3829d40 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
@@ -19,29 +19,153 @@
 
 package org.apache.helix.lock;
 
-import java.util.Map;
+import org.apache.helix.HelixProperty;
+import org.apache.helix.ZNRecord;
 
 
 /**
- * Generic interface for a map contains the Helix lock information
- * @param <K> The type of the LockInfo key
- * @param <V> the type of the LockInfo value
+ * Structure represents a lock node information, implemented using ZNRecord
  */
-public interface LockInfo<K, V> {
+public class LockInfo extends HelixProperty {
 
-  //TODO: add specific setter and getter for any field that is determined to be universal for all implementations of HelixLock
+  // Default values for each attribute if there are no current values set by user
+  public static final String DEFAULT_OWNER_TEXT = "";
+  public static final String DEFAULT_MESSAGE_TEXT = "";
+  public static final long DEFAULT_TIMEOUT_LONG = -1L;
+
+  // default lock info represents the status of a unlocked lock
+  public static final LockInfo defaultLockInfo = new LockInfo("");
+
+  /**
+   * The keys to lock information
+   */
+  public enum LockInfoAttribute {
+    OWNER, MESSAGE, TIMEOUT
+  }
+
+  /**
+   * Initialize a LockInfo with a ZNRecord id, set all info fields to default data
+   */
+  public LockInfo(String id) {
+    super(id);
+    resetLockInfo();
+  }
+
+  /**
+   * Initialize a LockInfo with a ZNRecord, set all info fields to default data
+   * @param znRecord The ZNRecord contains lock node data that used to initialize the LockInfo
+   */
+  public LockInfo(ZNRecord znRecord) {
+    super(znRecord);
+    setNullLockInfoFieldsToDefault();
+  }
+
+  /**
+   * Initialize a LockInfo with data for each field, set all null info fields to default data
+   * @param ownerId value of OWNER attribute
+   * @param message value of MESSAGE attribute
+   * @param timeout value of TIMEOUT attribute
+   */
+  public LockInfo(String ownerId, String message, long timeout) {
+    this(ownerId);
+    setLockInfoFields(ownerId, message, timeout);
+  }
+
+  /**
+   * Build a LOCKINFO instance that represents an unlocked lock states
+   * @return the unlocked lock node LockInfo instance
+   */
+  public static LockInfo buildUnlockedLockInfo() {
+    return new LockInfo("");
+  }
+
+  /**
+   * Set each field of lock info to user provided values if the values are not null, null values are set to default values
+   * @param ownerId value of OWNER attribute
+   * @param message value of MESSAGE attribute
+   * @param timeout value of TIMEOUT attribute
+   */
+  public void setLockInfoFields(String ownerId, String message, Long timeout) {
+    _record.setSimpleField(LockInfoAttribute.OWNER.name(),
+        ownerId == null ? DEFAULT_OWNER_TEXT : ownerId);
+    _record.setSimpleField(LockInfoAttribute.MESSAGE.name(),
+        message == null ? DEFAULT_MESSAGE_TEXT : message);
+    _record.setLongField(LockInfoAttribute.TIMEOUT.name(),
+        timeout == null ? DEFAULT_TIMEOUT_LONG : timeout);
+  }
+
+  /**
+   * Set all null values to default values in LockInfo, keep non-null values
+   */
+  private void setNullLockInfoFieldsToDefault() {
+    setLockInfoFields(getOwner(), getMessage(), getTimeout());
+  }
+
+  /**
+   * Reset the lock info to unlocked lock state
+   */
+  public void resetLockInfo() {
+    setLockInfoFields(DEFAULT_OWNER_TEXT, DEFAULT_MESSAGE_TEXT, DEFAULT_TIMEOUT_LONG);
+  }
+
+  /**
+   * Get the value for OWNER attribute of the lock
+   * @return the owner id of the lock, empty string if there is no owner id set
+   */
+  public String getOwner() {
+    String owner = _record.getSimpleField(LockInfoAttribute.OWNER.name());
+    return owner == null ? DEFAULT_OWNER_TEXT : owner;
+  }
+
+  /**
+   * Get the value for MESSAGE attribute of the lock
+   * @return the message of the lock, empty string if there is no message set
+   */
+  public String getMessage() {
+    String message = _record.getSimpleField(LockInfoAttribute.MESSAGE.name());
+    return message == null ? DEFAULT_MESSAGE_TEXT : message;
+  }
+
+  /**
+   * Get the value for TIMEOUT attribute of the lock
+   * @return the expiring time of the lock, -1 if there is no timeout set
+   */
+  public Long getTimeout() {
+    return _record.getLongField(LockInfoAttribute.TIMEOUT.name(), DEFAULT_TIMEOUT_LONG);
+  }
+
+  /**
+   * Get the value for OWNER attribute of the lock from a ZNRecord
+   * @return the owner id of the lock, empty string if there is no owner id set
+   */
+  static String getOwner(ZNRecord znRecord) {
+    if (znRecord == null) {
+      return DEFAULT_OWNER_TEXT;
+    }
+    String owner = znRecord.getSimpleField(LockInfoAttribute.OWNER.name());
+    return owner == null ? DEFAULT_OWNER_TEXT : owner;
+  }
 
   /**
-   * Create a single filed of LockInfo, or update the value of the field if it already exists
-   * @param infoKey the key of the LockInfo field
-   * @param infoValue the value of the LockInfo field
+   * Get the value for MESSAGE attribute of the lock from a ZNRecord
+   * @return the message of the lock, empty string if there is no message set
    */
-  void setInfoValue(K infoKey, V infoValue);
+  static String getMessage(ZNRecord znRecord) {
+    if (znRecord == null) {
+      return DEFAULT_MESSAGE_TEXT;
+    }
+    String message = znRecord.getSimpleField(LockInfoAttribute.MESSAGE.name());
+    return message == null ? DEFAULT_MESSAGE_TEXT : message;
+  }
 
   /**
-   * Get the value of a field in LockInfo
-   * @param infoKey the key of the LockInfo field
-   * @return the value of the field or null if this key does not exist
+   * Get the value for TIMEOUT attribute of the lock from a ZNRecord
+   * @return the expiring time of the lock, -1 if there is no timeout set
    */
-  V getInfoValue(K infoKey);
+  static long getTimeout(ZNRecord znRecord) {
+    if (znRecord == null) {
+      return DEFAULT_TIMEOUT_LONG;
+    }
+    return znRecord.getLongField(LockInfoAttribute.TIMEOUT.name(), DEFAULT_TIMEOUT_LONG);
+  }
 }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
index 18cac1a..b0cc8f1 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
@@ -20,6 +20,7 @@
 package org.apache.helix.lock;
 
 import java.util.Date;
+import java.util.concurrent.locks.Lock;
 
 import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
@@ -29,8 +30,9 @@ import org.apache.helix.ZNRecord;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.log4j.Logger;
 
-import static org.apache.helix.lock.ZKHelixNonblockingLockInfo.DEFAULT_OWNER_TEXT;
-import static org.apache.helix.lock.ZKHelixNonblockingLockInfo.DEFAULT_TIMEOUT_LONG;
+import static org.apache.helix.lock.LockInfo.DEFAULT_OWNER_TEXT;
+import static org.apache.helix.lock.LockInfo.DEFAULT_TIMEOUT_LONG;
+import static org.apache.helix.lock.LockInfo.defaultLockInfo;
 
 
 /**
@@ -40,7 +42,6 @@ public class ZKHelixNonblockingLock implements HelixLock {
 
   private static final Logger LOG = Logger.getLogger(ZKHelixNonblockingLock.class);
 
-  private static final String LOCK_ROOT = "/LOCK";
   private final String _lockPath;
   private final String _userId;
   private final long _timeout;
@@ -49,16 +50,15 @@ public class ZKHelixNonblockingLock implements HelixLock {
 
   /**
    * Initialize the lock with user provided information, e.g.,cluster, scope, etc.
-   * @param clusterName the cluster under which the lock will live
    * @param scope the scope to lock
    * @param zkAddress the zk address the cluster connects to
    * @param timeout the timeout period of the lcok
    * @param lockMsg the reason for having this lock
    * @param userId a universal unique userId for lock owner identity
    */
-  public ZKHelixNonblockingLock(String clusterName, HelixLockScope scope, String zkAddress,
-      Long timeout, String lockMsg, String userId) {
-    this("/" + clusterName.toUpperCase() + LOCK_ROOT + scope.getZkPath(), zkAddress, timeout, lockMsg, userId);
+  public ZKHelixNonblockingLock(HelixLockScope scope, String zkAddress, Long timeout,
+      String lockMsg, String userId) {
+    this(scope.getZkPath(), zkAddress, timeout, lockMsg, userId);
   }
 
   /**
@@ -82,10 +82,6 @@ public class ZKHelixNonblockingLock implements HelixLock {
   public boolean acquireLock() {
 
     // Set lock information fields
-    ZNRecord lockInfo = new ZNRecord(_userId);
-    lockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name(), _userId);
-    lockInfo.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE.name(), _lockMsg);
-
     long deadline;
     // Prevent value overflow
     if (_timeout > Long.MAX_VALUE - System.currentTimeMillis()) {
@@ -93,7 +89,8 @@ public class ZKHelixNonblockingLock implements HelixLock {
     } else {
       deadline = System.currentTimeMillis() + _timeout;
     }
-    lockInfo.setLongField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(), deadline);
+    LockInfo lockInfo = new LockInfo(_userId);
+    lockInfo.setLockInfoFields(_userId, _lockMsg, deadline);
 
     LockUpdater updater = new LockUpdater(lockInfo);
     return _baseDataAccessor.update(_lockPath, updater, AccessOption.PERSISTENT);
@@ -101,70 +98,64 @@ public class ZKHelixNonblockingLock implements HelixLock {
 
   @Override
   public boolean releaseLock() {
-    ZNRecord newLockInfo = new ZKHelixNonblockingLockInfo<>().toZNRecord();
-    LockUpdater updater = new LockUpdater(newLockInfo);
+    // Initialize the lock updater with a default lock info represents the state of a unlocked lock
+    LockUpdater updater = new LockUpdater(LockInfo.defaultLockInfo);
     return _baseDataAccessor.update(_lockPath, updater, AccessOption.PERSISTENT);
   }
 
   @Override
-  public ZKHelixNonblockingLockInfo getLockInfo() {
+  public LockInfo getLockInfo() {
     ZNRecord curLockInfo = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
-    return new ZKHelixNonblockingLockInfo(curLockInfo);
+    return new LockInfo(curLockInfo);
   }
 
   @Override
   public boolean isOwner() {
-    ZNRecord curLockInfo = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
-    return userIdMatches(curLockInfo) && !hasTimedOut(curLockInfo);
+    LockInfo lockInfo = getLockInfo();
+    return userIdMatches(lockInfo) && !hasTimedOut(lockInfo);
   }
 
   /**
    * Check if a lock has timed out
-   * @param record the current lock information in ZNRecord format
    * @return return true if the lock has timed out, otherwise return false.
    */
-  private boolean hasTimedOut(ZNRecord record) {
-    if (record == null) {
-      return false;
-    }
-    long timeout = record
-        .getLongField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(), DEFAULT_TIMEOUT_LONG);
-    return System.currentTimeMillis() >= timeout;
+  private boolean hasTimedOut(LockInfo lockInfo) {
+    return System.currentTimeMillis() >= lockInfo.getTimeout();
+  }
+
+  /**
+   * Check if a lock has timed out with lock information stored in a ZNRecord
+   * @return return true if the lock has timed out, otherwise return false.
+   */
+  private boolean hasTimedOut(ZNRecord znRecord) {
+    return System.currentTimeMillis() >= LockInfo.getTimeout(znRecord);
   }
 
   /**
    * Check if the current user Id matches with the owner Id in a lock info
-   * @param record the lock information in ZNRecord format
    * @return return true if the two ids match, otherwise return false.
    */
-  private boolean userIdMatches(ZNRecord record) {
-    if (record == null) {
-      return false;
-    }
-    String ownerId = record.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name());
-    return ownerId.equals(_userId);
+  private boolean userIdMatches(LockInfo lockInfo) {
+    return lockInfo.getOwner().equals(_userId);
   }
 
   /**
-   * Check if the lock node has a owner id field in the lock information
-   * @param record Lock information in format of ZNRecord
-   * @return true if the lock has a owner id field that the ownership can be or not be timed out, otherwise false
+   * Check if a user id in the ZNRecord matches current user's id
+   * @param znRecord ZNRecord contains lock information
+   * @return true if the ids match, false if not or ZNRecord does not contain user id information
    */
-  private boolean hasOwnerField(ZNRecord record) {
-    if (record == null) {
-      return false;
-    }
-    String ownerId = record.getSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name());
-    return ownerId != null && !ownerId.equals(DEFAULT_OWNER_TEXT);
+  private boolean userIdMatches(ZNRecord znRecord) {
+    return LockInfo.getOwner(znRecord).equals(_userId);
   }
 
   /**
    * Check if the lock node has a current owner
-   * @param record Lock information in format of ZNRecord
+   * @param znRecord Lock information in format of ZNRecord
    * @return true if the lock has a current owner that the ownership has not be timed out, otherwise false
    */
-  private boolean hasNonExpiredOwner(ZNRecord record) {
-    return hasOwnerField(record) && !hasTimedOut(record);
+  private boolean hasNonExpiredOwner(ZNRecord znRecord) {
+    String owner = LockInfo.getOwner(znRecord);
+    return !owner.equals(DEFAULT_OWNER_TEXT) && !hasTimedOut(znRecord);
   }
 
   /**
@@ -175,10 +166,10 @@ public class ZKHelixNonblockingLock implements HelixLock {
 
     /**
      * Initialize a structure for lock user to update a lock node value
-     * @param record the lock node value will be updated in ZNRecord format
+     * @param lockInfo the lock node value will be used to update the lock
      */
-    public LockUpdater(ZNRecord record) {
-      _record = record;
+    public LockUpdater(LockInfo lockInfo) {
+      _record = lockInfo.getRecord();
     }
 
     @Override
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
deleted file mode 100644
index 6e62717..0000000
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.helix.lock;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.helix.ZNRecord;
-
-
-public class ZKHelixNonblockingLockInfo<K extends ZKHelixNonblockingLockInfo.InfoKey, V extends String> implements LockInfo<K, V> {
-
-  public static final String DEFAULT_OWNER_TEXT = "";
-  public static final String DEFAULT_MESSAGE_TEXT = "";
-  public static final long DEFAULT_TIMEOUT_LONG = -1L;
-  public static final String DEFAULT_TIMEOUT_TEXT = String.valueOf(DEFAULT_TIMEOUT_LONG);
-  private Map<InfoKey, String> lockInfo;
-
-  public enum InfoKey {
-    OWNER, MESSAGE, TIMEOUT
-  }
-
-  /**
-   * Constructor of ZKHelixNonblockingLockInfo that set each field to default data
-   */
-  public ZKHelixNonblockingLockInfo() {
-    lockInfo = new HashMap<>();
-    lockInfo.put(InfoKey.OWNER, DEFAULT_OWNER_TEXT);
-    lockInfo.put(InfoKey.MESSAGE, DEFAULT_MESSAGE_TEXT);
-    lockInfo.put(InfoKey.TIMEOUT, DEFAULT_TIMEOUT_TEXT);
-  }
-
-  /**
-   * Construct a ZKHelixNonblockingLockInfo using a ZNRecord format of data
-   * @param znRecord A ZNRecord that contains lock information in its simple fields
-   */
-  public ZKHelixNonblockingLockInfo(ZNRecord znRecord) {
-    this();
-    if (znRecord == null) {
-      return;
-    }
-    Map<String, String> simpleFields = znRecord.getSimpleFields();
-    lockInfo
-        .put(InfoKey.OWNER, simpleFields.getOrDefault(InfoKey.OWNER.name(), DEFAULT_OWNER_TEXT));
-    lockInfo.put(InfoKey.MESSAGE,
-        simpleFields.getOrDefault(InfoKey.MESSAGE.name(), DEFAULT_MESSAGE_TEXT));
-    lockInfo.put(InfoKey.TIMEOUT,
-        simpleFields.getOrDefault(InfoKey.TIMEOUT.name(), DEFAULT_TIMEOUT_TEXT));
-  }
-
-  @Override
-  public void setInfoValue(InfoKey infoKey, String infoValue) {
-    lockInfo.put(infoKey, infoValue);
-  }
-
-  @Override
-  public String getInfoValue(InfoKey infoKey) {
-    return lockInfo.get(infoKey);
-  }
-
-  /**
-   * Method to convert a ZKHelixNonblockingLockInfo to ZNRecord
-   * @return a ZNRecord format data contains lock information in its simple fields
-   */
-  public ZNRecord toZNRecord() {
-    ZNRecord znRecord = new ZNRecord("");
-    znRecord.setSimpleField(InfoKey.OWNER.name(),
-        lockInfo.getOrDefault(InfoKey.OWNER, DEFAULT_OWNER_TEXT));
-    znRecord.setSimpleField(InfoKey.MESSAGE.name(),
-        lockInfo.getOrDefault(InfoKey.MESSAGE, DEFAULT_MESSAGE_TEXT));
-    znRecord.setSimpleField(InfoKey.TIMEOUT.name(),
-        lockInfo.getOrDefault(InfoKey.TIMEOUT, DEFAULT_TIMEOUT_TEXT));
-    return znRecord;
-  }
-}
diff --git a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
index 090a43d..5e28688 100644
--- a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
+++ b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
@@ -39,7 +39,6 @@ import org.testng.annotations.Test;
 public class TestZKHelixNonblockingLock extends ZkTestBase {
 
   private final String _clusterName = TestHelper.getTestClassName();
-  private final String _lockRoot = "/LOCK";
   private final String _lockMessage = "Test";
   private String _lockPath;
   private ZKHelixNonblockingLock _lock;
@@ -62,8 +61,8 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
     pathKeys.add("partition_name");
 
     _participantScope = new HelixLockScope(HelixLockScope.LockScopeProperty.PARTITION, pathKeys);
-    _lockPath = "/" + _clusterName.toUpperCase() + _lockRoot + _participantScope.getZkPath();
-    _lock = new ZKHelixNonblockingLock(_clusterName, _participantScope, ZK_ADDR, Long.MAX_VALUE,
+    _lockPath = _participantScope.getZkPath();
+    _lock = new ZKHelixNonblockingLock(_participantScope, ZK_ADDR, Long.MAX_VALUE,
         _lockMessage, _userId);
   }
 
@@ -81,9 +80,9 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
     Assert.assertTrue(_gZkClient.exists(_lockPath));
 
     // Get lock information
-    LockInfo<ZKHelixNonblockingLockInfo.InfoKey, String> record = _lock.getLockInfo();
-    Assert.assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.OWNER), _userId);
-    Assert.assertEquals(record.getInfoValue(ZKHelixNonblockingLockInfo.InfoKey.MESSAGE),
+    LockInfo lockInfo = _lock.getLockInfo();
+    Assert.assertEquals(lockInfo.getOwner(), _userId);
+    Assert.assertEquals(lockInfo.getMessage(),
         _lockMessage);
 
     // Check if the user is lock owner
@@ -100,8 +99,8 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
     // Fake condition when the lock owner is not current user
     String fakeUserID = UUID.randomUUID().toString();
     ZNRecord fakeRecord = new ZNRecord(fakeUserID);
-    fakeRecord.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name(), fakeUserID);
-    fakeRecord.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(),
+    fakeRecord.setSimpleField(LockInfo.LockInfoAttribute.OWNER.name(), fakeUserID);
+    fakeRecord.setSimpleField(LockInfo.LockInfoAttribute.TIMEOUT.name(),
         String.valueOf(Long.MAX_VALUE));
     _gZkClient.create(_lockPath, fakeRecord, CreateMode.PERSISTENT);
 
@@ -122,8 +121,8 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
     // Fake condition when the current lock already expired
     String fakeUserID = UUID.randomUUID().toString();
     ZNRecord fakeRecord = new ZNRecord(fakeUserID);
-    fakeRecord.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.OWNER.name(), fakeUserID);
-    fakeRecord.setSimpleField(ZKHelixNonblockingLockInfo.InfoKey.TIMEOUT.name(),
+    fakeRecord.setSimpleField(LockInfo.LockInfoAttribute.OWNER.name(), fakeUserID);
+    fakeRecord.setSimpleField(LockInfo.LockInfoAttribute.TIMEOUT.name(),
         String.valueOf(System.currentTimeMillis()));
     _gZkClient.create(_lockPath, fakeRecord, CreateMode.PERSISTENT);
 
@@ -141,7 +140,7 @@ public class TestZKHelixNonblockingLock extends ZkTestBase {
     List<Callable<Boolean>> threads = new ArrayList<>();
     for (int i = 0; i < 2; i++) {
       ZKHelixNonblockingLock lock =
-          new ZKHelixNonblockingLock(_clusterName, _participantScope, ZK_ADDR, Long.MAX_VALUE,
+          new ZKHelixNonblockingLock(_participantScope, ZK_ADDR, Long.MAX_VALUE,
               _lockMessage, UUID.randomUUID().toString());
       threads.add(new TestSimultaneousAcquireLock(lock));
     }


[helix] 18/19: Remove dependency of LockInfo on HelixProperty

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 89fddf3053844fcd9a0c582ffb7bd690388f2c62
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Tue Feb 18 14:21:15 2020 -0800

    Remove dependency of LockInfo on HelixProperty
---
 .../main/java/org/apache/helix/lock/LockInfo.java  | 29 ++++++++++------------
 .../helix/lock/helix/ZKHelixNonblockingLock.java   |  7 +++---
 2 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
index 250fb71..2c9a24f 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/LockInfo.java
@@ -19,14 +19,13 @@
 
 package org.apache.helix.lock;
 
-import org.apache.helix.HelixProperty;
 import org.apache.helix.ZNRecord;
 
 
 /**
  * Structure represents a lock node information, implemented using ZNRecord
  */
-public class LockInfo extends HelixProperty {
+public class LockInfo {
 
   // Default values for each attribute if there are no current values set by user
   public static final String DEFAULT_OWNER_TEXT = "";
@@ -37,6 +36,8 @@ public class LockInfo extends HelixProperty {
   public static final LockInfo defaultLockInfo =
       new LockInfo(DEFAULT_OWNER_TEXT, DEFAULT_MESSAGE_TEXT, DEFAULT_TIMEOUT_LONG);
 
+  private ZNRecord record;
+
   /**
    * The keys to lock information
    */
@@ -48,8 +49,8 @@ public class LockInfo extends HelixProperty {
    * Initialize a default LockInfo instance
    */
   private LockInfo() {
-    super("LOCK");
-    setLockInfoFields(null, null, DEFAULT_TIMEOUT_LONG);
+    record = new ZNRecord("LOCK");
+    setLockInfoFields(DEFAULT_OWNER_TEXT, DEFAULT_MESSAGE_TEXT, DEFAULT_TIMEOUT_LONG);
   }
 
   /**
@@ -84,11 +85,11 @@ public class LockInfo extends HelixProperty {
    * @param timeout value of TIMEOUT attribute
    */
   private void setLockInfoFields(String ownerId, String message, long timeout) {
-    _record.setSimpleField(LockInfoAttribute.OWNER.name(),
+    record.setSimpleField(LockInfoAttribute.OWNER.name(),
         ownerId == null ? DEFAULT_OWNER_TEXT : ownerId);
-    _record.setSimpleField(LockInfoAttribute.MESSAGE.name(),
+    record.setSimpleField(LockInfoAttribute.MESSAGE.name(),
         message == null ? DEFAULT_MESSAGE_TEXT : message);
-    _record.setLongField(LockInfoAttribute.TIMEOUT.name(), timeout);
+    record.setLongField(LockInfoAttribute.TIMEOUT.name(), timeout);
   }
 
   /**
@@ -96,7 +97,7 @@ public class LockInfo extends HelixProperty {
    * @return the owner id of the lock, empty string if there is no owner id set
    */
   public String getOwner() {
-    String owner = _record.getSimpleField(LockInfoAttribute.OWNER.name());
+    String owner = record.getSimpleField(LockInfoAttribute.OWNER.name());
     return owner == null ? DEFAULT_OWNER_TEXT : owner;
   }
 
@@ -105,7 +106,7 @@ public class LockInfo extends HelixProperty {
    * @return the message of the lock, empty string if there is no message set
    */
   public String getMessage() {
-    String message = _record.getSimpleField(LockInfoAttribute.MESSAGE.name());
+    String message = record.getSimpleField(LockInfoAttribute.MESSAGE.name());
     return message == null ? DEFAULT_MESSAGE_TEXT : message;
   }
 
@@ -114,14 +115,10 @@ public class LockInfo extends HelixProperty {
    * @return the expiring time of the lock, -1 if there is no timeout set
    */
   public Long getTimeout() {
-    return _record.getLongField(LockInfoAttribute.TIMEOUT.name(), DEFAULT_TIMEOUT_LONG);
+    return record.getLongField(LockInfoAttribute.TIMEOUT.name(), DEFAULT_TIMEOUT_LONG);
   }
 
-  /**
-   * Check if a lock has timed out
-   * @return return true if the lock has timed out, otherwise return false.
-   */
-  public boolean hasNotExpired() {
-    return System.currentTimeMillis() < getTimeout();
+  public ZNRecord getRecord() {
+    return record;
   }
 }
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java
index 41de4a7..cbfe9da 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/helix/ZKHelixNonblockingLock.java
@@ -110,7 +110,8 @@ public class ZKHelixNonblockingLock implements HelixLock {
   @Override
   public boolean isCurrentOwner() {
     LockInfo lockInfo = getCurrentLockInfo();
-    return lockInfo.getOwner().equals(_userId) && lockInfo.hasNotExpired();
+    return lockInfo.getOwner().equals(_userId) && (System.currentTimeMillis() < lockInfo
+        .getTimeout());
   }
 
   /**
@@ -132,12 +133,12 @@ public class ZKHelixNonblockingLock implements HelixLock {
       // If no one owns the lock, allow the update
       // If the user is the current lock owner, allow the update
       LockInfo curLockInfo = new LockInfo(current);
-      if (!curLockInfo.hasNotExpired() || isCurrentOwner()) {
+      if (!(System.currentTimeMillis() < curLockInfo.getTimeout()) || isCurrentOwner()) {
         return _record;
       }
       // For users who are not the lock owner and try to do an update on a lock that is held by someone else, exception thrown is to be caught by data accessor, and return false for the update
       LOG.error(
-          "User " + _userId + "tried to update the lock at " + new Date(System.currentTimeMillis())
+          "User " + _userId + " tried to update the lock at " + new Date(System.currentTimeMillis())
               + ". Lock path: " + _lockPath);
       throw new HelixException("User is not authorized to perform this operation.");
     }


[helix] 12/19: Fixed lock path generation

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 79ee132fd4912226ae0ddf5c3b5e7499b8bb2c01
Author: Molly Gao <mg...@mgao-ld1.linkedin.biz>
AuthorDate: Thu Feb 6 15:46:12 2020 -0800

    Fixed lock path generation
---
 .../java/org/apache/helix/lock/HelixLockScope.java | 160 +++++++++++++++++++++
 .../apache/helix/lock/ZKHelixNonblockingLock.java  |  10 +-
 .../helix/lock/TestZKHelixNonblockingLock.java     |  30 ++--
 3 files changed, 179 insertions(+), 21 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java
new file mode 100644
index 0000000..9fc12fc
--- /dev/null
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLockScope.java
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+package org.apache.helix.lock;
+
+import java.util.List;
+
+import org.apache.helix.util.StringTemplate;
+
+
+/**
+ *  Defines the various scopes of Helix locks, and how they are represented on Zookeeper
+ */
+public class HelixLockScope {
+
+  /**
+   * Define various properties of Helix lock, and associate them with the number of arguments required for getting znode path
+   */
+  public enum LockScopeProperty {
+
+    PARTICIPANT(1, 0),
+
+    RESOURCE(2, 1),
+
+    PARTITION(3, 2);
+
+    final int _zkPathArgNum;
+    final int _argumentPos;
+
+    private LockScopeProperty(int zkPathArgNum, int argumentPos) {
+      _zkPathArgNum = zkPathArgNum;
+      _argumentPos = argumentPos;
+    }
+
+    /**
+     * Get the number of template arguments required to generate a full path
+     * @return number of template arguments in the path
+     */
+    public int getZkPathArgNum() {
+      return _zkPathArgNum;
+    }
+
+    /**
+     * Get the position of this argument from the input that used to generate the scope
+     * @return the number of position of value for this property in the list of keys input
+     */
+    public int getArgumentPos() {
+      return _argumentPos;
+    }
+  }
+
+  /**
+   * string templates to generate znode path
+   */
+  private static final StringTemplate template = new StringTemplate();
+
+  static {
+    template.addEntry(HelixLockScope.LockScopeProperty.PARTICIPANT, 1, "/{participantName}");
+    template.addEntry(HelixLockScope.LockScopeProperty.RESOURCE, 2,
+        "/{participantName}/{resourceName}");
+    template.addEntry(HelixLockScope.LockScopeProperty.PARTITION, 3,
+        "/{participantName}/{resourceName}/{partitionName}");
+  }
+
+  private final HelixLockScope.LockScopeProperty _type;
+  private final String _participantName;
+  private final String _resourceName;
+  private final String _partitionName;
+
+  private final String _zkPath;
+
+  /**
+   * Initialize with a type of scope and unique identifiers
+   * @param type the scope
+   * @param zkPathKeys keys identifying a ZNode location
+   */
+  public HelixLockScope(HelixLockScope.LockScopeProperty type, List<String> zkPathKeys) {
+
+    if (zkPathKeys.size() != type.getZkPathArgNum()) {
+      throw new IllegalArgumentException(
+          type + " requires " + type.getZkPathArgNum() + " arguments to get znode, but was: "
+              + zkPathKeys);
+    }
+
+    _type = type;
+
+    //Initialize the name fields for various scope
+    _participantName = zkPathKeys.get(LockScopeProperty.PARTICIPANT.getArgumentPos());
+
+    if (type.getZkPathArgNum() >= LockScopeProperty.RESOURCE.getZkPathArgNum()) {
+      _resourceName = zkPathKeys.get(LockScopeProperty.RESOURCE.getArgumentPos());
+    } else {
+      _resourceName = null;
+    }
+
+    if (type.getZkPathArgNum() >= LockScopeProperty.PARTITION.getZkPathArgNum()) {
+      _partitionName = zkPathKeys.get(LockScopeProperty.PARTITION.getArgumentPos());
+    } else {
+      _partitionName = null;
+    }
+
+    _zkPath = template.instantiate(type, zkPathKeys.toArray(new String[0]));
+  }
+
+  /**
+   * Get the scope
+   * @return the type of scope
+   */
+  public HelixLockScope.LockScopeProperty getType() {
+    return _type;
+  }
+
+  /**
+   * Get the participant name if it exists
+   * @return the participant name
+   */
+  public String getParticipantName() {
+    return _participantName;
+  }
+
+  /**
+   * Get the resource name if it exists
+   * @return the resource name
+   */
+  public String getResourceName() {
+    return _resourceName;
+  }
+
+  /**
+   * Get the partition name if it exists
+   * @return the partition name
+   */
+  public String getPartitionName() {
+    return _partitionName;
+  }
+
+  /**
+   * Get the path to the corresponding ZNode
+   * @return a Zookeeper path
+   */
+  public String getZkPath() {
+    return _zkPath;
+  }
+}
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
index 0424177..0573228 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
@@ -28,6 +28,8 @@ import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.HelixException;
+import org.apache.helix.PropertyPathBuilder;
+import org.apache.helix.PropertyType;
 import org.apache.helix.ZNRecord;
 import org.apache.helix.api.exceptions.HelixMetaDataAccessException;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
@@ -48,8 +50,7 @@ public class ZKHelixNonblockingLock implements HelixLock {
 
   private static final Logger LOG = Logger.getLogger(ZKHelixNonblockingLock.class);
 
-  private static final String LOCK_ROOT = "LOCKS";
-  private static final String PATH_DELIMITER = "/";
+  private static final String LOCK_ROOT = "/LOCK";
   private final String _lockPath;
   private final String _userId;
   private final long _timeout;
@@ -65,10 +66,9 @@ public class ZKHelixNonblockingLock implements HelixLock {
    * @param lockMsg the reason for having this lock
    * @param userId a universal unique userId for lock owner identity
    */
-  public ZKHelixNonblockingLock(String clusterName, HelixConfigScope scope, String zkAddress,
+  public ZKHelixNonblockingLock(String clusterName, HelixLockScope scope, String zkAddress,
       Long timeout, String lockMsg, String userId) {
-    this(PATH_DELIMITER +  String.join(PATH_DELIMITER, clusterName, LOCK_ROOT, scope.getZkPath()), zkAddress, timeout, lockMsg,
-        userId);
+    this("/" + clusterName + LOCK_ROOT + scope.getZkPath(), zkAddress, timeout, lockMsg, userId);
   }
 
   /**
diff --git a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
index 29f84ae..96a15b3 100644
--- a/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
+++ b/helix-lock/src/test/java/org/apache/helix/lock/TestZKHelixNonblockingLock.java
@@ -26,17 +26,12 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 
-import org.apache.helix.AccessOption;
 import org.apache.helix.PropertyPathBuilder;
+import org.apache.helix.PropertyType;
 import org.apache.helix.TestHelper;
 import org.apache.helix.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
-import org.apache.helix.manager.zk.TestWtCacheAsyncOpMultiThread;
-import org.apache.helix.manager.zk.ZKHelixDataAccessor;
-import org.apache.helix.manager.zk.ZkCacheBaseDataAccessor;
-import org.apache.helix.model.HelixConfigScope;
-import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty;
-import org.apache.helix.model.builder.HelixConfigScopeBuilder;
+import org.apache.helix.common.ZkTestBase;
 import org.apache.zookeeper.CreateMode;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
@@ -44,16 +39,15 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 
-public class TestZKHelixNonblockingLock extends ZkUnitTestBase {
+public class TestZKHelixNonblockingLock extends ZkTestBase {
 
-  private final String _className = TestHelper.getTestClassName();
-  private final String _methodName = TestHelper.getTestMethodName();
-  private final String _clusterName = _className + "_" + _methodName;
+  private final String _clusterName = TestHelper.getTestClassName();
+  private final String _lockRoot = "/LOCK";
   private final String _lockMessage = "Test";
   private String _lockPath;
   private ZKHelixNonblockingLock _lock;
   private String _userId;
-  private HelixConfigScope _participantScope;
+  private HelixLockScope _participantScope;
 
   @BeforeClass
   public void beforeClass() throws Exception {
@@ -63,10 +57,14 @@ public class TestZKHelixNonblockingLock extends ZkUnitTestBase {
     TestHelper.setupCluster(_clusterName, ZK_ADDR, 12918, "localhost", "TestDB", 1, 10, 5, 3,
         "MasterSlave", true);
     _userId = UUID.randomUUID().toString();
-    _participantScope =
-        new HelixConfigScopeBuilder(ConfigScopeProperty.PARTICIPANT).forCluster(_clusterName)
-            .forParticipant("localhost_12918").build();
-    _lockPath = "/" + _clusterName + '/' + "LOCKS" + '/' + _participantScope.getZkPath();
+
+    List<String> pathKeys = new ArrayList<>();
+    pathKeys.add("participant_name");
+    pathKeys.add("resource_name");
+    pathKeys.add("partition_name");
+
+    _participantScope = new HelixLockScope(HelixLockScope.LockScopeProperty.PARTITION, pathKeys);
+    _lockPath = "/" + _clusterName + _lockRoot + _participantScope.getZkPath();
     _lock = new ZKHelixNonblockingLock(_clusterName, _participantScope, ZK_ADDR, Long.MAX_VALUE,
         _lockMessage, _userId);
   }


[helix] 06/19: created Helix nonblocking lock based on zk

Posted by jx...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jxue pushed a commit to branch distributed-lock
in repository https://gitbox.apache.org/repos/asf/helix.git

commit 9621a8b554ff908230a8a27e6a71ad588bc0b1ae
Author: Molly Gao <mg...@mgao-mn1.linkedin.biz>
AuthorDate: Tue Jan 28 18:16:45 2020 -0800

    created Helix nonblocking lock based on zk
---
 .../main/java/org/apache/helix/lock/HelixLock.java |   4 +-
 .../apache/helix/lock/ZKHelixNonblockingLock.java  | 135 +++++++++++++++++++++
 .../helix/lock/ZKHelixNonblockingLockInfo.java     |  50 ++++++++
 3 files changed, 187 insertions(+), 2 deletions(-)

diff --git a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
index 01ef63b..71b1ca6 100644
--- a/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
+++ b/helix-lock/src/main/java/org/apache/helix/lock/HelixLock.java
@@ -22,7 +22,7 @@ package org.apache.helix.lock;
 /**
  * Generic interface for Helix distributed lock
  */
-public interface HelixLock<T> {
+public interface HelixLock {
   /**
    * Blocking call to acquire a lock
    * @return true if the lock was successfully acquired,
@@ -42,7 +42,7 @@ public interface HelixLock<T> {
    * Retrieve the lock information, e.g. lock timeout, lock message, etc.
    * @return lock metadata information
    */
-  LockInfo<T> getLockInfo();
+  <T> LockInfo<T> getLockInfo();
 
   /**
    * If the user is current lock owner
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
new file mode 100644
index 0000000..75dd0e5
--- /dev/null
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLock.java
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+package org.apache.helix.lock;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.helix.AccessOption;
+import org.apache.helix.BaseDataAccessor;
+import org.apache.helix.ZNRecord;
+import org.apache.helix.manager.zk.ZNRecordSerializer;
+import org.apache.helix.manager.zk.ZkBaseDataAccessor;
+import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.model.HelixConfigScope;
+import org.apache.helix.util.ZNRecordUtil;
+import org.apache.log4j.Logger;
+
+
+/**
+ * Helix nonblocking lock implementation based on Zookeeper
+ */
+public class ZKHelixNonblockingLock implements HelixLock {
+
+  private static final Logger LOG = Logger.getLogger(ZKHelixNonblockingLock.class);
+
+  private static final String LOCK_ROOT = "LOCKS";
+  private final String _lockPath;
+  private final String _userID;
+  private final long _timeout;
+  private final String _lockMsg;
+  private final BaseDataAccessor<ZNRecord> _baseDataAccessor;
+
+  /**
+   * Initialize the lock with user provided information, e.g.,cluster, scope, etc.
+   * @param clusterName the cluster under which the lock will live
+   * @param scope the scope to lock
+   * @param zkAddress the zk address the cluster connects to
+   * @param timeout the timeout period of the lcok
+   * @param lockMsg the reason for having this lock
+   */
+  public ZKHelixNonblockingLock(String clusterName, HelixConfigScope scope, String zkAddress,
+      Long timeout, String lockMsg, String userID) {
+    this("/" + clusterName + '/' + LOCK_ROOT + '/' + scope, zkAddress, timeout, lockMsg, userID);
+  }
+
+  /**
+   * Initialize the lock with user provided information, e.g., lock path under zookeeper, etc.
+   * @param lockPath the path of the lock under Zookeeper
+   * @param zkAddress the zk address of the cluster
+   * @param timeout the timeout period of the lcok
+   * @param lockMsg the reason for having this lock
+   */
+  public ZKHelixNonblockingLock(String lockPath, String zkAddress, Long timeout, String lockMsg,
+      String userID) {
+    HelixZkClient zkClient = new ZkClient(zkAddress);
+    _lockPath = lockPath;
+    _timeout = timeout;
+    _lockMsg = lockMsg;
+    _userID = userID;
+    _baseDataAccessor = new ZkBaseDataAccessor<ZNRecord>(zkClient.getServers());
+  }
+
+  @Override
+  public boolean acquireLock() {
+
+    // Set lock information fields
+    ZNRecord lockInfo = new ZNRecord(_userID);
+    lockInfo.setSimpleField("Owner", _userID);
+    lockInfo.setSimpleField("message", _lockMsg);
+    long timeout = System.currentTimeMillis() + _timeout;
+    lockInfo.setSimpleField("timeout", String.valueOf(timeout));
+
+    // Try to create the lock node
+    boolean success = _baseDataAccessor.create(_lockPath, lockInfo, AccessOption.PERSISTENT);
+
+    // If fail to create the lock node, compare the timeout timestamp of current lock node with current time, if already passes the timeout, delete current lock node and try to create lock node again
+    if (!success) {
+      ZNRecord curLock = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
+      long curTimeout = Long.parseLong(curLock.getSimpleField("timeout"));
+      if (System.currentTimeMillis() >= curTimeout) {
+        _baseDataAccessor.remove(_lockPath, AccessOption.PERSISTENT);
+        return _baseDataAccessor.create(_lockPath, lockInfo, AccessOption.PERSISTENT);
+      }
+    }
+    return success;
+  }
+
+  @Override
+  public boolean releaseLock() {
+    if (isOwner()) {
+      return _baseDataAccessor.remove(_lockPath, AccessOption.PERSISTENT);
+    }
+    return false;
+  }
+
+  @Override
+  public LockInfo<String> getLockInfo() {
+    ZKHelixNonblockingLockInfo<String> lockInfo = new ZKHelixNonblockingLockInfo<>();
+    ZNRecord curLockInfo = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
+    lockInfo.setLockInfoFields(curLockInfo);
+    return lockInfo;
+  }
+
+  @Override
+  public boolean isOwner() {
+    ZNRecord curLockInfo = _baseDataAccessor.get(_lockPath, null, AccessOption.PERSISTENT);
+    if (curLockInfo == null) {
+      return false;
+    }
+    String ownerID = curLockInfo.getSimpleField("owner");
+    if (ownerID == null) {
+      return false;
+    }
+    return ownerID.equals(_userID);
+  }
+}
diff --git a/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
new file mode 100644
index 0000000..d54c386
--- /dev/null
+++ b/helix-lock/src/main/java/org/apache/helix/lock/ZKHelixNonblockingLockInfo.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package org.apache.helix.lock;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.helix.ZNRecord;
+
+
+public class ZKHelixNonblockingLockInfo<T extends String> implements LockInfo<T> {
+
+  private Map<String, String>  lockInfo;
+
+  public ZKHelixNonblockingLockInfo() {
+    lockInfo = new HashMap<>();
+  }
+
+  @Override
+  public void setInfoValue(String infoKey, String infoValue) {
+    lockInfo.put(infoKey, infoValue);
+  }
+
+  @Override
+  public T getInfoValue(String infoKey) {
+    return (T)lockInfo.get(infoKey);
+  }
+
+  public void setLockInfoFields(ZNRecord record) {
+    Map<String, String> recordSimpleFields = record.getSimpleFields();
+    lockInfo.putAll(recordSimpleFields);
+  }
+}