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 | 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"); | ... | ... |