Commit f50dec4f7642cf417b3c4b1cd420595f0741b30f

Authored by YevhenBondarenko
1 parent 4c4ec36b

fix ConstraintViolationException on findOrCreateDeviceProfile

... ... @@ -64,8 +64,10 @@ import java.util.Arrays;
64 64 import java.util.Collections;
65 65 import java.util.HashSet;
66 66 import java.util.List;
67   -import java.util.stream.Collectors;
68 67 import java.util.Set;
  68 +import java.util.concurrent.locks.Lock;
  69 +import java.util.concurrent.locks.ReentrantLock;
  70 +import java.util.stream.Collectors;
69 71
70 72 import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE;
71 73 import static org.thingsboard.server.dao.service.Validator.validateId;
... ... @@ -101,6 +103,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
101 103 @Autowired
102 104 private CacheManager cacheManager;
103 105
  106 + private final Lock findOrCreateLock = new ReentrantLock();
  107 +
104 108 @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId.id}")
105 109 @Override
106 110 public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId) {
... ... @@ -220,7 +224,15 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
220 224 log.trace("Executing findOrCreateDefaultDeviceProfile");
221 225 DeviceProfile deviceProfile = findDeviceProfileByName(tenantId, name);
222 226 if (deviceProfile == null) {
223   - deviceProfile = this.doCreateDefaultDeviceProfile(tenantId, name, name.equals("default"));
  227 + try {
  228 + findOrCreateLock.lock();
  229 + deviceProfile = findDeviceProfileByName(tenantId, name);
  230 + if (deviceProfile == null) {
  231 + deviceProfile = this.doCreateDefaultDeviceProfile(tenantId, name, name.equals("default"));
  232 + }
  233 + } finally {
  234 + findOrCreateLock.unlock();
  235 + }
224 236 }
225 237 return deviceProfile;
226 238 }
... ...
... ... @@ -15,6 +15,10 @@
15 15 */
16 16 package org.thingsboard.server.dao.service;
17 17
  18 +import com.google.common.util.concurrent.Futures;
  19 +import com.google.common.util.concurrent.ListenableFuture;
  20 +import com.google.common.util.concurrent.ListeningExecutorService;
  21 +import com.google.common.util.concurrent.MoreExecutors;
18 22 import org.junit.After;
19 23 import org.junit.Assert;
20 24 import org.junit.Before;
... ... @@ -34,6 +38,9 @@ import org.thingsboard.server.dao.exception.DataValidationException;
34 38 import java.util.ArrayList;
35 39 import java.util.Collections;
36 40 import java.util.List;
  41 +import java.util.concurrent.ExecutionException;
  42 +import java.util.concurrent.ExecutorService;
  43 +import java.util.concurrent.Executors;
37 44 import java.util.stream.Collectors;
38 45
39 46 public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
... ... @@ -113,6 +120,23 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
113 120 }
114 121
115 122 @Test
  123 + public void testFindOrCreateDeviceProfile() throws ExecutionException, InterruptedException {
  124 + ListeningExecutorService testExecutor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(100));
  125 + try {
  126 + List<ListenableFuture<DeviceProfile>> futures = new ArrayList<>();
  127 + for (int i = 0; i < 50; i ++) {
  128 + futures.add(testExecutor.submit(() -> deviceProfileService.findOrCreateDeviceProfile(tenantId, "Device Profile 1")));
  129 + futures.add(testExecutor.submit(() -> deviceProfileService.findOrCreateDeviceProfile(tenantId, "Device Profile 2")));
  130 + }
  131 +
  132 + List<DeviceProfile> deviceProfiles = Futures.allAsList(futures).get();
  133 + deviceProfiles.forEach(Assert::assertNotNull);
  134 + } finally {
  135 + testExecutor.shutdownNow();
  136 + }
  137 + }
  138 +
  139 + @Test
116 140 public void testSetDefaultDeviceProfile() {
117 141 DeviceProfile deviceProfile1 = this.createDeviceProfile(tenantId,"Device Profile 1");
118 142 DeviceProfile deviceProfile2 = this.createDeviceProfile(tenantId,"Device Profile 2");
... ...