Browse Source

Used DB lock instead of Java lock

pull/3557/head
vzikratyi 6 years ago
parent
commit
9eb9302ee3
  1. 1
      application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
  2. 30
      common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockKey.java
  3. 25
      common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockService.java
  4. 43
      dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java
  5. 91
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java
  6. 20
      dao/src/main/java/org/thingsboard/server/dao/sql/lock/LockRepository.java
  7. 42
      dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java

1
application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java

@ -43,7 +43,6 @@ public class OAuth2Controller extends BaseController {
@Autowired @Autowired
private OAuth2Service oauth2Service; private OAuth2Service oauth2Service;
// TODO ask why POST
@RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST)
@ResponseBody @ResponseBody
public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request) throws ThingsboardException {

30
common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockKey.java

@ -0,0 +1,30 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* 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.
*/
package org.thingsboard.server.dao.lock;
public enum LockKey {
OAUTH2_CONFIG(999);
private int id;
LockKey(int id) {
this.id = id;
}
public int getId() {
return id;
}
}

25
common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockService.java

@ -0,0 +1,25 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* 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.
*/
package org.thingsboard.server.dao.lock;
public interface LockService {
/**
* Lock with SQL Database till the end of transaction
*
* @param key identifier
*/
void transactionLock(LockKey key);
}

43
dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java

@ -0,0 +1,43 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* 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.
*/
package org.thingsboard.server.dao.lock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.sql.lock.LockRepository;
import javax.annotation.PostConstruct;
@Service
@Slf4j
public class LockServiceImpl implements LockService {
@Autowired(required = false)
private LockRepository lockRepository;
@PostConstruct
public void init(){
log.warn("Locking with DB is not enabled.");
}
@Override
public void transactionLock(LockKey key) {
if (lockRepository == null) return;
log.trace("Locking transaction key [{}]", key);
lockRepository.transactionLock(key.getId());
}
}

91
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java

@ -1,12 +1,12 @@
/** /**
* Copyright © 2016-2020 The Thingsboard Authors * Copyright © 2016-2020 The Thingsboard Authors
* <p> *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* <p> *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* <p> *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -28,6 +28,7 @@ import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.*; import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.id.*;
@ -36,13 +37,14 @@ import org.thingsboard.server.common.data.oauth2.*;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.lock.LockKey;
import org.thingsboard.server.dao.lock.LockService;
import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -51,11 +53,8 @@ import static org.thingsboard.server.dao.oauth2.OAuth2Utils.*;
@Slf4j @Slf4j
@Service @Service
public class OAuth2ServiceImpl implements OAuth2Service { public class OAuth2ServiceImpl implements OAuth2Service {
private static final ObjectMapper mapper = new ObjectMapper(); private static final ObjectMapper mapper = new ObjectMapper();
private final ReentrantLock clientRegistrationSaveLock = new ReentrantLock();
@Autowired @Autowired
private Environment environment; private Environment environment;
@ -68,6 +67,9 @@ public class OAuth2ServiceImpl implements OAuth2Service {
@Autowired @Autowired
private TenantService tenantService; private TenantService tenantService;
@Autowired
private LockService lockService;
private boolean isInstall() { private boolean isInstall() {
return environment.acceptsProfiles("install"); return environment.acceptsProfiles("install");
} }
@ -123,52 +125,59 @@ public class OAuth2ServiceImpl implements OAuth2Service {
@Override @Override
public OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) { public OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) {
validate(oAuth2ClientsParams); validate(oAuth2ClientsParams);
validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID);
clientRegistrationSaveLock.lock();
try { transactionalSaveSystemOAuth2ClientsParams(oAuth2ClientsParams);
validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID);
AdminSettings oauth2SystemAdminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS);
if (oauth2SystemAdminSettings == null) {
oauth2SystemAdminSettings = createSystemAdminSettings();
}
String json = toJson(oAuth2ClientsParams);
((ObjectNode) oauth2SystemAdminSettings.getJsonValue()).put(SYSTEM_SETTINGS_OAUTH2_VALUE, json);
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, oauth2SystemAdminSettings);
} finally {
clientRegistrationSaveLock.unlock();
}
return getSystemOAuth2ClientsParams(); return getSystemOAuth2ClientsParams();
} }
@Transactional
private void transactionalSaveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) {
long acquireStart = System.currentTimeMillis();
lockService.transactionLock(LockKey.OAUTH2_CONFIG);
log.trace("[{}] Waited for lock {} ms.", TenantId.SYS_TENANT_ID, System.currentTimeMillis() - acquireStart);
validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID);
AdminSettings oauth2SystemAdminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS);
if (oauth2SystemAdminSettings == null) {
oauth2SystemAdminSettings = createSystemAdminSettings();
}
String json = toJson(oAuth2ClientsParams);
((ObjectNode) oauth2SystemAdminSettings.getJsonValue()).put(SYSTEM_SETTINGS_OAUTH2_VALUE, json);
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, oauth2SystemAdminSettings);
}
@Override @Override
public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) {
validate(oAuth2ClientsParams); validate(oAuth2ClientsParams);
validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId);
clientRegistrationSaveLock.lock();
try {
validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId);
Set<String> domainNames = oAuth2ClientsParams.getClientsDomainsParams().stream() transactionalSaveTenantOAuth2ClientsParams(tenantId, oAuth2ClientsParams);
.map(OAuth2ClientsDomainParams::getDomainName)
.collect(Collectors.toSet());
processTenantAdminSettings(tenantId, domainNames);
List<AttributeKvEntry> attributes = createOAuth2ClientsParamsAttributes(oAuth2ClientsParams); return getTenantOAuth2ClientsParams(tenantId);
try { }
attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get();
} catch (Exception e) {
log.error("Unable to save OAuth2 Client Registration Params to attributes!", e);
throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!");
}
} finally { @Transactional
clientRegistrationSaveLock.unlock(); private void transactionalSaveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) {
} long acquireStart = System.currentTimeMillis();
lockService.transactionLock(LockKey.OAUTH2_CONFIG);
log.trace("[{}] Waited for lock {} ms.", tenantId, System.currentTimeMillis() - acquireStart);
return getTenantOAuth2ClientsParams(tenantId); validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId);
Set<String> domainNames = oAuth2ClientsParams.getClientsDomainsParams().stream()
.map(OAuth2ClientsDomainParams::getDomainName)
.collect(Collectors.toSet());
processTenantAdminSettings(tenantId, domainNames);
List<AttributeKvEntry> attributes = createOAuth2ClientsParamsAttributes(oAuth2ClientsParams);
try {
attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get();
} catch (Exception e) {
log.error("Unable to save OAuth2 Client Registration Params to attributes!", e);
throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!");
}
} }
private List<AttributeKvEntry> createOAuth2ClientsParamsAttributes(OAuth2ClientsParams oAuth2ClientsParams) { private List<AttributeKvEntry> createOAuth2ClientsParamsAttributes(OAuth2ClientsParams oAuth2ClientsParams) {

20
dao/src/main/java/org/thingsboard/server/dao/sql/lock/LockRepository.java

@ -0,0 +1,20 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* 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.
*/
package org.thingsboard.server.dao.sql.lock;
public interface LockRepository {
void transactionLock(Integer key);
}

42
dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* 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.
*/
package org.thingsboard.server.dao.sql.lock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.thingsboard.server.dao.util.PsqlDao;
import org.thingsboard.server.dao.util.SqlDao;
@Slf4j
@SqlDao
@PsqlDao
@Repository
public class PsqlLockRepository implements LockRepository {
private static final String TRANSACTION_LOCK_QUERY = "SELECT pg_advisory_xact_lock(?)";
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void transactionLock(Integer key) {
jdbcTemplate.query(TRANSACTION_LOCK_QUERY,
preparedStatement -> preparedStatement.setInt(1, key),
resultSet -> {}
);
}
}
Loading…
Cancel
Save