Commit bb643df296650cfbe420512490339f39b6661668
Committed by
GitHub
Merge pull request #3888 from YevhenBondarenko/fix-device-profile-service
fix ConstraintViolationException on findOrCreateDeviceProfile
Showing
2 changed files
with
38 additions
and
2 deletions
@@ -64,8 +64,10 @@ import java.util.Arrays; | @@ -64,8 +64,10 @@ import java.util.Arrays; | ||
64 | import java.util.Collections; | 64 | import java.util.Collections; |
65 | import java.util.HashSet; | 65 | import java.util.HashSet; |
66 | import java.util.List; | 66 | import java.util.List; |
67 | -import java.util.stream.Collectors; | ||
68 | import java.util.Set; | 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 | import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE; | 72 | import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE; |
71 | import static org.thingsboard.server.dao.service.Validator.validateId; | 73 | import static org.thingsboard.server.dao.service.Validator.validateId; |
@@ -101,6 +103,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | @@ -101,6 +103,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | ||
101 | @Autowired | 103 | @Autowired |
102 | private CacheManager cacheManager; | 104 | private CacheManager cacheManager; |
103 | 105 | ||
106 | + private final Lock findOrCreateLock = new ReentrantLock(); | ||
107 | + | ||
104 | @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId.id}") | 108 | @Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#deviceProfileId.id}") |
105 | @Override | 109 | @Override |
106 | public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId) { | 110 | public DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId) { |
@@ -220,7 +224,15 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | @@ -220,7 +224,15 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | ||
220 | log.trace("Executing findOrCreateDefaultDeviceProfile"); | 224 | log.trace("Executing findOrCreateDefaultDeviceProfile"); |
221 | DeviceProfile deviceProfile = findDeviceProfileByName(tenantId, name); | 225 | DeviceProfile deviceProfile = findDeviceProfileByName(tenantId, name); |
222 | if (deviceProfile == null) { | 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 | return deviceProfile; | 237 | return deviceProfile; |
226 | } | 238 | } |
@@ -15,6 +15,10 @@ | @@ -15,6 +15,10 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.service; | 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 | import org.junit.After; | 22 | import org.junit.After; |
19 | import org.junit.Assert; | 23 | import org.junit.Assert; |
20 | import org.junit.Before; | 24 | import org.junit.Before; |
@@ -34,6 +38,9 @@ import org.thingsboard.server.dao.exception.DataValidationException; | @@ -34,6 +38,9 @@ import org.thingsboard.server.dao.exception.DataValidationException; | ||
34 | import java.util.ArrayList; | 38 | import java.util.ArrayList; |
35 | import java.util.Collections; | 39 | import java.util.Collections; |
36 | import java.util.List; | 40 | import java.util.List; |
41 | +import java.util.concurrent.ExecutionException; | ||
42 | +import java.util.concurrent.ExecutorService; | ||
43 | +import java.util.concurrent.Executors; | ||
37 | import java.util.stream.Collectors; | 44 | import java.util.stream.Collectors; |
38 | 45 | ||
39 | public class BaseDeviceProfileServiceTest extends AbstractServiceTest { | 46 | public class BaseDeviceProfileServiceTest extends AbstractServiceTest { |
@@ -113,6 +120,23 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { | @@ -113,6 +120,23 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { | ||
113 | } | 120 | } |
114 | 121 | ||
115 | @Test | 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 | public void testSetDefaultDeviceProfile() { | 140 | public void testSetDefaultDeviceProfile() { |
117 | DeviceProfile deviceProfile1 = this.createDeviceProfile(tenantId,"Device Profile 1"); | 141 | DeviceProfile deviceProfile1 = this.createDeviceProfile(tenantId,"Device Profile 1"); |
118 | DeviceProfile deviceProfile2 = this.createDeviceProfile(tenantId,"Device Profile 2"); | 142 | DeviceProfile deviceProfile2 = this.createDeviceProfile(tenantId,"Device Profile 2"); |