Skip to content

Commit

Permalink
improve ranking transactions handling
Browse files Browse the repository at this point in the history
  • Loading branch information
GoldRenard committed Nov 22, 2019
1 parent e9a86bc commit b7baca2
Show file tree
Hide file tree
Showing 17 changed files with 248 additions and 160 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ subprojects {
apply plugin: 'java-library'
apply plugin: 'idea'
apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'io.spring.dependency-management'

sourceCompatibility = JavaVersion.VERSION_11
Expand All @@ -83,6 +84,7 @@ subprojects {
kotlinVersion = "$kotlinVersion"
springCloudVersion = 'Greenwich.RELEASE'
springConsulVersion = '2.1.2.RELEASE'
springRetryVersion = "1.2.4.RELEASE"
hibernateVersion = '5.3.7.Final'
lombokVersion = '1.18.4'
jsonVersion = '20160810'
Expand Down
1 change: 0 additions & 1 deletion jb-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import org.apache.tools.ant.filters.ReplaceTokens

apply plugin: 'application'
apply plugin: 'kotlin-spring'
apply plugin: 'org.springframework.boot'

description = 'JuniperBot Discord Bot Web Application'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import ru.juniperbot.common.persistence.entity.GuildConfig;
import ru.juniperbot.common.persistence.repository.CommandReactionRepository;
import ru.juniperbot.common.persistence.repository.CustomCommandRepository;
import ru.juniperbot.common.service.TransactionHandler;
import ru.juniperbot.common.utils.CommonUtils;
import ru.juniperbot.common.worker.feature.service.FeatureSetService;
import ru.juniperbot.common.worker.message.model.MessageTemplateCompiler;
Expand Down Expand Up @@ -64,6 +65,9 @@ public class CustomCommandsServiceImpl extends BaseCommandsService {
@Autowired
private FeatureSetService featureSetService;

@Autowired
private TransactionHandler transactionHandler;

@Override
public boolean sendCommand(GuildMessageReceivedEvent event, String content, String key, GuildConfig config) {
Guild guild = event.getGuild();
Expand Down Expand Up @@ -154,7 +158,8 @@ private void registerReactions(Message message, CustomCommand command) {
log.error("Could not add reaction emote", e);
}
});
contextService.inTransaction(() -> {

transactionHandler.runInTransaction(() -> {
commandRepository.findById(command.getId())
.ifPresent(e -> reactionRepository.save(new CommandReaction(message, e)));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ public interface ContextService {

void initContext(long guildId);

void inTransaction(Runnable action);

void resetContext();

<T> void queue(Guild guild, RestAction<T> action, Consumer<T> success);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import ru.juniperbot.common.configuration.CommonProperties;
import ru.juniperbot.common.service.ConfigService;
import ru.juniperbot.common.service.TransactionHandler;
import ru.juniperbot.common.utils.LocaleUtils;

import java.awt.*;
Expand Down Expand Up @@ -76,7 +75,7 @@ private static class ContextHolder {
private SourceResolverService resolverService;

@Autowired
private TransactionTemplate transactionTemplate;
private TransactionHandler transactionHandler;

@Override
public Locale getLocale() {
Expand Down Expand Up @@ -245,25 +244,7 @@ private <T> void withContext(T value, Consumer<T> init, Runnable action) {
@Override
@Async
public void withContextAsync(Guild guild, Runnable action) {
inTransaction(() -> withContext(guild, action));
}

@Override
public void inTransaction(Runnable action) {
try {
transactionTemplate.execute(status -> {
try {
action.run();
} catch (ObjectOptimisticLockingFailureException e) {
throw e;
} catch (Exception e) {
log.error("Async task results in error", e);
}
return null;
});
} catch (ObjectOptimisticLockingFailureException e) {
log.warn("Optimistic locking failed for object {} [id={}]", e.getPersistentClassName(), e.getIdentifier());
}
transactionHandler.runInTransaction(() -> withContext(guild, action));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import ru.juniperbot.common.persistence.entity.MemberWarning;
import ru.juniperbot.common.persistence.repository.MemberWarningRepository;
import ru.juniperbot.common.worker.event.service.ContextService;
import ru.juniperbot.common.service.TransactionHandler;
import ru.juniperbot.common.worker.shared.support.AbstractJob;

public class UnWarnJob extends AbstractJob {
Expand All @@ -33,13 +33,13 @@ public class UnWarnJob extends AbstractJob {
private MemberWarningRepository repository;

@Autowired
private ContextService contextService;
private TransactionHandler transactionHandler;

@Override
public void execute(JobExecutionContext jobExecutionContext) {
JobDataMap data = jobExecutionContext.getJobDetail().getJobDataMap();
long warningId = data.getLongFromString(ATTR_WARNING_ID);
contextService.inTransaction(() -> repository.flushWarning(warningId));
transactionHandler.runInTransaction(() -> repository.flushWarning(warningId));
}

public static JobDetail createDetails(@NonNull MemberWarning warning) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import ru.juniperbot.common.persistence.entity.MuteState;
import ru.juniperbot.common.persistence.repository.MuteStateRepository;
import ru.juniperbot.common.service.ModerationConfigService;
import ru.juniperbot.common.service.TransactionHandler;
import ru.juniperbot.common.worker.event.service.ContextService;
import ru.juniperbot.common.worker.jobs.UnMuteJob;
import ru.juniperbot.common.worker.modules.audit.model.AuditActionBuilder;
Expand Down Expand Up @@ -70,6 +71,9 @@ private enum PermissionMode {
@Autowired
private AuditService auditService;

@Autowired
private TransactionHandler transactionHandler;

@Override
@Transactional
public Role getMutedRole(Guild guild) {
Expand Down Expand Up @@ -130,19 +134,17 @@ public boolean mute(ModerationActionRequest request) {
.withAttribute(DURATION_MS_ATTR, request.getDuration())
.withAttribute(GLOBAL_ATTR, request.isGlobal()) : null;

Consumer<Object> schedule = g -> {
contextService.inTransaction(() -> {
if (!request.isStateless()) {
if (request.getDuration() != null) {
scheduleUnMute(request);
}
storeState(request);
Consumer<Object> schedule = g -> transactionHandler.runInTransaction(() -> {
if (!request.isStateless()) {
if (request.getDuration() != null) {
scheduleUnMute(request);
}
if (actionBuilder != null) {
actionBuilder.save();
}
});
};
storeState(request);
}
if (actionBuilder != null) {
actionBuilder.save();
}
});

if (request.isGlobal()) {
Guild guild = request.getGuild();
Expand Down
33 changes: 19 additions & 14 deletions jb-common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,43 @@ dependencies {
api "org.springframework.boot:spring-boot-starter-amqp"
api "org.springframework:spring-web"
api "org.springframework:spring-context-support"
api "org.springframework.retry:spring-retry:$springRetryVersion"

// COMMON CORE DEPENDENCIES
api "org.apache.commons:commons-lang3"
api "commons-io:commons-io:${commonsIoVersion}"
api "commons-validator:commons-validator:${commonsValidatorVersion}"
api "commons-codec:commons-codec:${commonsCodecVersion}"
api "org.apache.commons:commons-collections4:${commonsCollections4Version}"
api "commons-io:commons-io:$commonsIoVersion"
api "commons-validator:commons-validator:$commonsValidatorVersion"
api "commons-codec:commons-codec:$commonsCodecVersion"
api "org.apache.commons:commons-collections4:$commonsCollections4Version"
api "joda-time:joda-time"
api "com.google.code.gson:gson:${gsonVersion}"
api "com.google.guava:guava:${guavaVersion}"
api "org.json:json:${jsonVersion}"
api "com.google.code.gson:gson:$gsonVersion"
api "com.google.guava:guava:$guavaVersion"
api "org.json:json:$jsonVersion"
api "com.fasterxml.jackson.core:jackson-core"
api "com.fasterxml.jackson.core:jackson-databind"
api "com.github.jasminb:jsonapi-converter:${jsonApiConverter}"
api "org.ocpsoft.prettytime:prettytime:${prettytimeVersion}"
api "com.github.jasminb:jsonapi-converter:$jsonApiConverter"
api "org.ocpsoft.prettytime:prettytime:$prettytimeVersion"

// DISCORD DEPENDENCIES
api ("net.dv8tion:JDA:${jdaVersion}") {
api ("net.dv8tion:JDA:$jdaVersion") {
exclude group: 'club.minnced', module: 'opus-java'
}
api "club.minnced:discord-webhooks:${discordWebhookVersion}"
api "com.google.apis:google-api-services-youtube:${googleApiServicesYoutubeVersion}"
api "club.minnced:discord-webhooks:$discordWebhookVersion"
api "com.google.apis:google-api-services-youtube:$googleApiServicesYoutubeVersion"

// DATABASE DEPENDENCIES
implementation "javax.annotation:javax.annotation-api"
implementation "org.hibernate.validator:hibernate-validator"
implementation "org.postgresql:postgresql:${postgresqlVersion}"
implementation "org.postgresql:postgresql:$postgresqlVersion"
implementation "org.liquibase:liquibase-core"

testImplementation "org.springframework.boot:spring-boot-starter-test"
}

dependencyManagement {
imports { mavenBom("org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}") }
imports {
mavenBom("org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion") {
bomProperty("kotlin.version", "$kotlinVersion")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.springframework.context.annotation.*;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
Expand All @@ -34,6 +35,7 @@
import ru.juniperbot.common.support.jmx.ThreadPoolTaskExecutorMBean;

@EnableAsync
@EnableRetry
@EnableScheduling
@EnableAspectJAutoProxy
@EnableTransactionManagement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,4 @@ public interface DomainService<T extends GuildEntity> {
boolean isCacheable();

void setCacheable(boolean cacheable);

void inTransaction(Runnable action);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* This file is part of JuniperBot.
*
* JuniperBot is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* JuniperBot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with JuniperBot. If not, see <http://www.gnu.org/licenses/>.
*/
package ru.juniperbot.common.service

interface TransactionHandler {

fun runInTransaction(action: Runnable)

fun runInNewTransaction(action: Runnable)

fun runWithLockRetry(action: Runnable)

fun <T> runInTransaction(action: () -> T): T

fun <T> runInNewTransaction(action: () -> T): T

fun <T> runWithLockRetry(action: () -> T): T
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@
import net.dv8tion.jda.api.entities.Guild;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import ru.juniperbot.common.persistence.entity.base.GuildEntity;
import ru.juniperbot.common.persistence.repository.base.GuildRepository;
import ru.juniperbot.common.service.DomainService;
Expand All @@ -50,9 +46,6 @@ public abstract class AbstractDomainServiceImpl<T extends GuildEntity, R extends
@Autowired
protected ApplicationContext applicationContext;

@Autowired
protected TransactionTemplate transactionTemplate;

@Autowired
protected GatewayService gatewayService;

Expand Down Expand Up @@ -124,20 +117,6 @@ public T getOrCreate(long guildId) {
return result;
}

@Override
public void inTransaction(Runnable action) {
try {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
action.run();
}
});
} catch (ObjectOptimisticLockingFailureException e) {
log.warn("Optimistic locking failed for object {} [id={}]", e.getPersistentClassName(), e.getIdentifier());
}
}

protected abstract T createNew(long guildId);

protected abstract Class<T> getDomainClass();
Expand Down
Loading

0 comments on commit b7baca2

Please sign in to comment.