PageRenderTime 39ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/solr/core/src/test/org/apache/solr/cloud/api/collections/CollectionApiLockingTest.java

http://github.com/apache/solr
Java | 149 lines | 82 code | 31 blank | 36 comment | 3 complexity | 799ad957b351ebf40a58fc287966aff4 MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package org.apache.solr.cloud.api.collections;
  18. import java.util.concurrent.CountDownLatch;
  19. import org.apache.solr.SolrTestCaseJ4;
  20. import org.apache.solr.cloud.DistributedMultiLock;
  21. import org.apache.solr.cloud.ZkDistributedCollectionLockFactory;
  22. import org.apache.solr.cloud.ZkTestServer;
  23. import org.apache.solr.common.cloud.SolrZkClient;
  24. import org.apache.solr.common.params.CollectionParams;
  25. import org.junit.Test;
  26. public class CollectionApiLockingTest extends SolrTestCaseJ4 {
  27. private static final String COLLECTION_NAME = "lockColl";
  28. final String SHARD1_NAME = "lockShard1";
  29. final String SHARD2_NAME = "lockShard2";
  30. final String REPLICA_NAME = "lockReplica";
  31. static final int TIMEOUT = 10000;
  32. /**
  33. * Tests the Collection API locking. All tests run from single test method to save on setup time.
  34. */
  35. @Test
  36. public void monothreadedApiLockTests() throws Exception {
  37. ZkTestServer server = new ZkTestServer(createTempDir("zkData"));
  38. try {
  39. server.run();
  40. try (SolrZkClient zkClient = new SolrZkClient(server.getZkAddress(), TIMEOUT)) {
  41. CollectionApiLockFactory apiLockFactory = new CollectionApiLockFactory(new ZkDistributedCollectionLockFactory(zkClient, "/apiLockTestRoot"));
  42. monothreadedTests(apiLockFactory);
  43. multithreadedTests(apiLockFactory);
  44. }
  45. } finally {
  46. server.shutdown();
  47. }
  48. }
  49. private void monothreadedTests(CollectionApiLockFactory apiLockingHelper) throws Exception {
  50. // Lock at collection level (which prevents locking + acquiring on any other level of the hierarchy)
  51. DistributedMultiLock collLock = apiLockingHelper.createCollectionApiLock(CollectionParams.LockLevel.COLLECTION, COLLECTION_NAME, null, null);
  52. assertTrue("Collection should have been acquired", collLock.isAcquired());
  53. assertEquals("Lock at collection level expected to need one distributed lock", 1, collLock.getCountInternalLocks());
  54. // Request a shard lock. Will not be acquired as long as we don't release the collection lock above
  55. DistributedMultiLock shard1Lock = apiLockingHelper.createCollectionApiLock(CollectionParams.LockLevel.SHARD, COLLECTION_NAME, SHARD1_NAME, null);
  56. assertFalse("Shard1 should not have been acquired", shard1Lock.isAcquired());
  57. assertEquals("Lock at shard level expected to need two distributed locks", 2, shard1Lock.getCountInternalLocks());
  58. // Request a lock on another shard. Will not be acquired as long as we don't release the collection lock above
  59. DistributedMultiLock shard2Lock = apiLockingHelper.createCollectionApiLock(CollectionParams.LockLevel.SHARD, COLLECTION_NAME, SHARD2_NAME, null);
  60. assertFalse("Shard2 should not have been acquired", shard2Lock.isAcquired());
  61. assertTrue("Collection should still be acquired", collLock.isAcquired());
  62. collLock.release();
  63. assertTrue("Shard1 should have been acquired now that collection lock released", shard1Lock.isAcquired());
  64. assertTrue("Shard2 should have been acquired now that collection lock released", shard2Lock.isAcquired());
  65. // Request a lock on replica of shard1
  66. DistributedMultiLock replicaShard1Lock = apiLockingHelper.createCollectionApiLock(CollectionParams.LockLevel.SHARD, COLLECTION_NAME, SHARD1_NAME, REPLICA_NAME);
  67. assertFalse("replicaShard1Lock should not have been acquired, shard1 is locked", replicaShard1Lock.isAcquired());
  68. // Now ask for a new lock on the collection
  69. collLock = apiLockingHelper.createCollectionApiLock(CollectionParams.LockLevel.COLLECTION, COLLECTION_NAME, null, null);
  70. assertFalse("Collection should not have been acquired, shard1 and shard2 locks preventing it", collLock.isAcquired());
  71. shard1Lock.release();
  72. assertTrue("replicaShard1Lock should have been acquired, as shard1 got released", replicaShard1Lock.isAcquired());
  73. assertFalse("Collection should not have been acquired, shard2 lock is preventing it", collLock.isAcquired());
  74. replicaShard1Lock.release();
  75. // Request a lock on replica of shard2
  76. DistributedMultiLock replicaShard2Lock = apiLockingHelper.createCollectionApiLock(CollectionParams.LockLevel.SHARD, COLLECTION_NAME, SHARD2_NAME, REPLICA_NAME);
  77. assertFalse("replicaShard2Lock should not have been acquired, shard2 is locked", replicaShard2Lock.isAcquired());
  78. shard2Lock.release();
  79. assertTrue("Collection should have been acquired as shard2 got released and replicaShard2Locks was requested after the collection lock", collLock.isAcquired());
  80. assertFalse("replicaShard2Lock should not have been acquired, collLock is locked", replicaShard2Lock.isAcquired());
  81. collLock.release();
  82. assertTrue("replicaShard2Lock should have been acquired, the collection lock got released", replicaShard2Lock.isAcquired());
  83. // Release remaining lock to allow the multithreaded locking to succeed
  84. replicaShard2Lock.release();
  85. }
  86. private void multithreadedTests(CollectionApiLockFactory apiLockingHelper) throws Exception {
  87. // Lock on collection...
  88. DistributedMultiLock collLock = apiLockingHelper.createCollectionApiLock(CollectionParams.LockLevel.COLLECTION, COLLECTION_NAME, null, null);
  89. assertTrue("Collection should have been acquired", collLock.isAcquired());
  90. // ...blocks a lock on replica from being acquired
  91. final DistributedMultiLock replicaShard1Lock = apiLockingHelper.createCollectionApiLock(CollectionParams.LockLevel.SHARD, COLLECTION_NAME, SHARD1_NAME, REPLICA_NAME);
  92. assertFalse("replicaShard1Lock should not have been acquired, because collection is locked", replicaShard1Lock.isAcquired());
  93. // Wait for acquisition of the replica lock on another thread (and be notified via a latch)
  94. final CountDownLatch latch = new CountDownLatch(1);
  95. new Thread(() -> {
  96. replicaShard1Lock.waitUntilAcquired();
  97. // countDown() will not be called if waitUntilAcquired() threw exception of any kind
  98. latch.countDown();
  99. }).start();
  100. // Wait for the thread to start and to get blocked in waitUntilAcquired()
  101. // (thread start could have been checked more reliably using another latch, and verifying the thread is in waitUntilAcquired
  102. // done through that thread stacktrace, but that would be overkill compared to the very slight race condition of waiting 30ms,
  103. // but a race that would not cause the test to fail since we're testing... that nothing happened yet).
  104. Thread.sleep(30);
  105. assertEquals("we should not have been notified that replica was acquired", 1, latch.getCount());
  106. assertFalse("replica lock should not have been acquired", replicaShard1Lock.isAcquired());
  107. collLock.release();
  108. assertTrue("replica lock should have been acquired now that collection lock was released", replicaShard1Lock.isAcquired());
  109. // Wait for the Zookeeper watch to fire + the thread to be unblocked and countdown the latch
  110. // We'll wait up to 10 seconds here, so should be safe even if GC is extraordinarily high with a pause
  111. int i = 0;
  112. while (i < 1000 && latch.getCount() != 0) {
  113. Thread.sleep(10);
  114. i++;
  115. }
  116. assertEquals("we should have been notified that replica lock was acquired", 0, latch.getCount());
  117. }
  118. }