...
 
Commits (6)
/target/
!.mvn/wrapper/maven-wrapper.jar
/backup/
/axon/
### STS ###
.apt_generated
......
FROM azul/zulu-openjdk:8u192
COPY ./target/app.jar .
COPY ./config/application.yaml .
ENV JAVA_OPTS="-Xmx128m"
ENV SERVER_PORT=80
EXPOSE 80
CMD java $JAVA_OPTS -jar /app.jar
\ No newline at end of file
#!/usr/bin/env bash
docker build -t axon-backup:latest .
\ No newline at end of file
spring:
datasource:
url: jdbc:h2:/controldata/axondb-controldb
axoniq:
axondb:
domain: local
controldb-backup-location: /controldata/
file:
storage: /events
max-segment-size: 1_000_000
server:
context-path: /axondb
\ No newline at end of file
version: '3.4'
services:
axondb-1:
image: eu.gcr.io/tradingengine-194513/blox-infra/axondb:1.3.9-1
hostname: axondb-1
volumes:
- ./config/axondb.yml:/axondb.yml
- ./axon/axondb1/events:/events
- ./axon/axondb1/controldata:/controldata
# entrypoint: cat /axondb.yml
# environment:
# JAVA_OPTS: "--spring.config.location=/axondb.yml"
ports:
- "8023:8023"
- "8123:8123"
......@@ -80,7 +80,7 @@
</dependencies>
<build>
<finalName>axondb-backup-client</finalName>
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
......
......@@ -6,11 +6,7 @@ Idea behind this client is to schedule AxonDB backups and store them in predefin
Client periodically calls AxonDB to check if there is new closed event files.
If there is one or more it creates a controlDB backup file and copies it and new events files to preconfigured locations.
This client should be ran near AxonDB since controlDB backups are stored in root dir.
In case of ordinary machine/virtual machine deployment just run it as a separate process.
In case of docker use `nohup java -jar backup.jar --spring.config.location=/axondb-backup.yml &` in your run script to run it in background to AxonDB.
Client can run as a separate pod in kubernetes cluster as long as controldb/events disks are mounted to client in RO mode.
## Restore
To restore copy all files events backups to corresponding folders in AxonDB.
......
package nl.trifork.axondbbackupclient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
import org.springframework.boot.autoconfigure.SpringBootApplication;
......@@ -10,10 +11,12 @@ import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableScheduling
@AutoConfigurationPackage
public class AxonDbBackupClientApplication {
@Slf4j
public class AxonDbBackupClient {
public static void main(String[] args) {
SpringApplication.run(AxonDbBackupClientApplication.class, args);
SpringApplication.run(AxonDbBackupClient.class, args);
log.info("Backup client started");
}
@Bean
......
......@@ -2,11 +2,14 @@ package nl.trifork.axondbbackupclient;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import nl.trifork.axondbbackupclient.backup.BackupJob;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
import static nl.trifork.axondbbackupclient.util.FileUtils.ensureDir;
@Data
@ConfigurationProperties(prefix = "backup")
......@@ -14,20 +17,20 @@ import javax.annotation.PostConstruct;
@Slf4j
public class BackupConfig {
private String axondbHost;
private String eventsUrl;
private String snapshotsUrl;
private String dbDumpUrl;
private String controlDbUrl;
private String axondbToken;
private String backupPath;
private String axonPath;
private String eventsBackupPath;
private String snapshotsBackupPath;
private String dbDumpBackupPath;
private List<BackupJob> jobs;
@PostConstruct
public void print() {
log.info(toString());
ensureDir(backupPath);
jobs.forEach(j -> ensureDir(backupPath + j.getName()));
}
}
package nl.trifork.axondbbackupclient.backup;
import lombok.Data;
@Data
public class BackupJob {
private String name;
private String axondbToken;
private String axondbHost;
private String eventsPath;
private String snapshotsPath;
private String controlDbPath;
}
package nl.trifork.axondbbackupclient.backup;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import nl.trifork.axondbbackupclient.BackupConfig;
import org.springframework.stereotype.Service;
import static nl.trifork.axondbbackupclient.util.FileUtils.ensureDir;
@Service
@Slf4j
@RequiredArgsConstructor
public class BackupService {
private final ControlDBBackupService controlDb;
private final SnapshotsBackupService segments;
private final EventsBackupService events;
private final BackupConfig config;
public void run(BackupJob job) {
ensureDirs(job);
controlDb.backup(job);
segments.backup(job);
events.backup(job);
}
private void ensureDirs(BackupJob job) {
String eventsDir = config.getBackupPath() + job.getName() + "/events";
ensureDir(eventsDir);
ensureDir(eventsDir + "/closed");
}
}
package nl.trifork.axondbbackupclient.backup_services;
package nl.trifork.axondbbackupclient.backup;
import com.google.common.base.Stopwatch;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import nl.trifork.axondbbackupclient.BackupConfig;
import nl.trifork.axondbbackupclient.axondb_client.AxonDbRestClient;
import nl.trifork.axondbbackupclient.client.AxonDbRestClient;
import org.springframework.stereotype.Service;
import java.io.File;
import static com.google.common.base.Stopwatch.createStarted;
import static nl.trifork.axondbbackupclient.util.FileUtils.copy;
import static nl.trifork.axondbbackupclient.util.FileUtils.filenameFromPath;
@Service
@Slf4j
......@@ -18,21 +20,27 @@ public class ControlDBBackupService {
private final BackupConfig config;
private final AxonDbRestClient axonDbClient;
private final FileIoService fileService;
public void backup() {
public void backup(BackupJob job) {
Stopwatch stopwatch = createStarted();
File dbDump = new File(axonDbClient.createControlDbDump());
String dumpPath = axonDbClient.createControlDbDump(job.getAxondbHost());
File dbDump = dbDump(job, dumpPath);
String pathToSave = pathToSave(job);
if (dbDump.exists()) {
try {
fileService.copy(dbDump, config.getDbDumpBackupPath());
log.info("Created and copied db dump in {}", stopwatch);
} finally {
dbDump.delete();
}
copy(dbDump, pathToSave);
log.info("Created and copied db dump {} for {} to {} in {}", dbDump.getName(), job.getName(), pathToSave, stopwatch);
}
}
private File dbDump(BackupJob job, String dumpPath) {
String path = config.getAxonPath() + job.getName() + job.getControlDbPath() + filenameFromPath(dumpPath);
return new File(path);
}
private String pathToSave(BackupJob job) {
return config.getBackupPath() + job.getName() + "/";
}
}
package nl.trifork.axondbbackupclient.backup;
import com.google.common.base.Stopwatch;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import nl.trifork.axondbbackupclient.BackupConfig;
import nl.trifork.axondbbackupclient.client.AxonDbRestClient;
import nl.trifork.axondbbackupclient.util.FileUtils;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import static com.google.common.base.Stopwatch.createStarted;
import static java.nio.file.Files.list;
import static java.util.stream.Collectors.toList;
import static nl.trifork.axondbbackupclient.util.FileUtils.copy;
import static nl.trifork.axondbbackupclient.util.FileUtils.copyWithOverride;
@Service
@RequiredArgsConstructor
@Slf4j
public class EventsBackupService {
private final BackupConfig config;
private final AxonDbRestClient axonDbClient;
//todo extract similar logic to snapshots
public void backup(BackupJob job) {
Stopwatch stopwatch = createStarted();
List<File> closedEvents = closedEvents(job);
log.trace("Closed events size {}", closedEvents.size());
copyClosed(job, closedEvents);
List<File> currentSnapshots = currentEvents(job, closedEvents);
log.trace("Current snapshots {}", currentSnapshots);
copyCurrent(job, currentSnapshots);
log.trace("Copied events in {}", stopwatch);
}
private List<File> closedEvents(BackupJob job) {
List<String> closed = axonDbClient.getClosedEvents(job.getAxondbHost()).stream()
.map(FileUtils::filenameFromPath)
.collect(toList());
try {
Path dir = Paths.get(config.getAxonPath() + job.getName() + job.getEventsPath());
return list(dir)
.map(Path::toFile)
.filter(f -> !f.getName().endsWith(".snapshots"))
.filter(f -> closed.contains(f.getName()))
.collect(toList());
} catch (IOException e) {
log.error("Failed to read events dir", e);
throw new RuntimeException(e);
}
}
private void copyClosed(BackupJob job, List<File> closed) {
String dirTo = config.getBackupPath() + job.getName() + "/events/closed/";
closed.forEach(f -> copy(f, dirTo));
}
private List<File> currentEvents(BackupJob job, List<File> closed) {
try {
Path dir = Paths.get(config.getAxonPath() + job.getName() + job.getEventsPath());
return list(dir)
.map(Path::toFile)
.filter(f -> !f.getName().endsWith(".snapshots"))
.filter(f -> !closed.contains(f))
.collect(toList());
} catch (IOException e) {
log.error("Failed to read events dir", e);
throw new RuntimeException(e);
}
}
private void copyCurrent(BackupJob job, List<File> currentSnapshots) {
String dirTo = config.getBackupPath() + job.getName() + "/events/";
currentSnapshots.forEach(f -> copyWithOverride(f, dirTo));
}
}
package nl.trifork.axondbbackupclient.backup;
import com.google.common.base.Stopwatch;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import nl.trifork.axondbbackupclient.BackupConfig;
import nl.trifork.axondbbackupclient.client.AxonDbRestClient;
import nl.trifork.axondbbackupclient.util.FileUtils;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import static com.google.common.base.Stopwatch.createStarted;
import static java.nio.file.Files.list;
import static java.util.stream.Collectors.toList;
import static nl.trifork.axondbbackupclient.util.FileUtils.copy;
import static nl.trifork.axondbbackupclient.util.FileUtils.copyWithOverride;
@Service
@RequiredArgsConstructor
@Slf4j
public class SnapshotsBackupService {
private final BackupConfig config;
private final AxonDbRestClient axonDbClient;
public void backup(BackupJob job) {
Stopwatch stopwatch = createStarted();
List<File> closedSnapshots = closedSnapshots(job);
log.trace("Closed snapshots size {}", closedSnapshots.size());
copyClosed(job, closedSnapshots);
List<File> currentSnapshots = currentSnapshots(job, closedSnapshots);
log.trace("Current snapshots {}", currentSnapshots);
copyCurrent(job, currentSnapshots);
log.trace("Copied snapshots in {}", stopwatch);
}
private List<File> closedSnapshots(BackupJob job) {
List<String> closed = axonDbClient.getClosedSnapshots(job.getAxondbHost()).stream()
.map(FileUtils::filenameFromPath)
.collect(toList());
try {
Path dir = Paths.get(config.getAxonPath() + job.getName() + job.getSnapshotsPath());
return list(dir)
.map(Path::toFile)
.filter(f -> f.getName().endsWith(".snapshots"))
.filter(f -> closed.contains(f.getName()))
.collect(toList());
} catch (IOException e) {
log.error("Failed to read events dir", e);
throw new RuntimeException(e);
}
}
private void copyClosed(BackupJob job, List<File> closedSnapshots) {
String dirTo = config.getBackupPath() + job.getName() + "/events/closed/";
closedSnapshots.forEach(f -> copy(f, dirTo));
}
private List<File> currentSnapshots(BackupJob job, List<File> closedSnapshots) {
try {
Path dir = Paths.get(config.getAxonPath() + job.getName() + job.getSnapshotsPath());
return list(dir)
.map(Path::toFile)
.filter(f -> f.getName().endsWith(".snapshots"))
.filter(f -> !closedSnapshots.contains(f))
.collect(toList());
} catch (IOException e) {
log.error("Failed to read snapshots dir", e);
throw new RuntimeException(e);
}
}
private void copyCurrent(BackupJob job, List<File> currentSnapshots) {
String dirTo = config.getBackupPath() + job.getName() + "/events/";
currentSnapshots.forEach(f -> copyWithOverride(f, dirTo));
}
}
package nl.trifork.axondbbackupclient.backup_services;
import com.google.common.base.Stopwatch;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import nl.trifork.axondbbackupclient.BackupConfig;
import nl.trifork.axondbbackupclient.axondb_client.AxonDbRestClient;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.io.File;
import static com.google.common.base.Stopwatch.createStarted;
import static java.util.Arrays.stream;
@Service
@RequiredArgsConstructor
@Slf4j
public class SegmentsBackupService {
private final BackupConfig config;
private final AxonDbRestClient axonDbClient;
private final ControlDBBackupService dbBackupService;
private final FileIoService fileService;
@Scheduled(fixedDelayString = "${backup.fixedDelayMs}", initialDelayString = "${backup.initialDelayMs}")
public void backup() {
if (hasNewSegments()) {
dbBackupService.backup();
backupEvents();
backupSnapshots();
} else {
log.debug("No new segments");
}
}
private boolean hasNewSegments() {
String[] segments = axonDbClient.getClosedEventSegments();
return stream(segments)
.map(File::new)
.filter(File::exists)
.map(File::getName)
.filter(name -> name.endsWith(".events"))
.map(name -> config.getEventsBackupPath() + name)
.map(File::new)
.filter(f -> !f.exists())
.peek(f -> log.info("Found new segment {}", f.getName()))
.count() > 0;
}
private void backupEvents() {
Stopwatch stopwatch = createStarted();
String[] segments = axonDbClient.getClosedEventSegments();
stream(segments)
.map(File::new)
.filter(File::exists)
.forEach(f -> fileService.copy(f, config.getEventsBackupPath()));
log.info("Copied event segments in {}", stopwatch);
}
private void backupSnapshots() {
Stopwatch stopwatch = createStarted();
String[] segments = axonDbClient.getClosedSnapshotsSegments();
stream(segments)
.map(File::new)
.filter(File::exists)
.forEach(f -> fileService.copy(f, config.getSnapshotsBackupPath()));
log.info("Copied snapshot segments in {}", stopwatch);
}
}
package nl.trifork.axondbbackupclient.axondb_client;
package nl.trifork.axondbbackupclient.client;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
......@@ -9,6 +9,9 @@ import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import static java.util.Arrays.asList;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
......@@ -20,44 +23,48 @@ public class AxonDbRestClient {
private final RestTemplate restTemplate;
private final BackupConfig config;
public String[] getClosedEventSegments() {
return getReadySegments(eventsUrl());
public List<String> getClosedEvents(String host) {
return asList(getReadySegments(eventsUrl(host)));
}
public String[] getClosedSnapshotsSegments() {
return getReadySegments(snapshotsUrl());
public List<String> getClosedSnapshots(String host) {
return asList(getReadySegments(snapshotsUrl(host)));
}
private String[] getReadySegments(String url) {
try {
log.trace("Calling for ready segments on {}", url);
ResponseEntity<String[]> segments = restTemplate.exchange(url, GET, reqEntity(), String[].class);
return segments.getBody();
} catch (RuntimeException e) {
log.error("Failed to get ready segments on {}", url, e);
log.error("Failed to get ready segments from {}", url, e);
return new String[0];
}
}
public String createControlDbDump() {
ResponseEntity<String> dbDumpPath = restTemplate.exchange(dbDumpUrl(), POST, reqEntity(), String.class);
public String createControlDbDump(String host) {
String url = dbDumpUrl(host);
log.trace("Calling for controldb backup on {}", url);
ResponseEntity<String> dbDumpPath = restTemplate.exchange(url, POST, reqEntity(), String.class);
log.trace("Controldb backup created on {}", dbDumpPath.getBody());
return dbDumpPath.getBody();
}
private String eventsUrl() {
return config.getAxondbHost() + config.getEventsUrl();
private String eventsUrl(String host) {
return host + config.getEventsUrl();
}
private String snapshotsUrl() {
return config.getAxondbHost() + config.getSnapshotsUrl();
private String snapshotsUrl(String host) {
return host + config.getSnapshotsUrl();
}
private String dbDumpUrl() {
return config.getAxondbHost() + config.getDbDumpUrl();
private String dbDumpUrl(String host) {
return host + config.getControlDbUrl();
}
private HttpEntity reqEntity() {
HttpHeaders headers = new HttpHeaders();
headers.add("Access-Token", config.getAxondbToken());
headers.add("Access-Token", config.getJobs().get(0).getAxondbToken());
HttpEntity<String> entity = new HttpEntity<>(headers);
return entity;
}
......
package nl.trifork.axondbbackupclient.cron;
import com.google.common.base.Stopwatch;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import nl.trifork.axondbbackupclient.BackupConfig;
import nl.trifork.axondbbackupclient.backup.BackupJob;
import nl.trifork.axondbbackupclient.backup.BackupService;
import org.slf4j.MDC;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import static java.util.concurrent.Executors.newFixedThreadPool;
import static nl.trifork.axondbbackupclient.cron.BackupStatus.DONE;
import static nl.trifork.axondbbackupclient.cron.BackupStatus.ERROR;
import static nl.trifork.axondbbackupclient.cron.BackupStatus.RUNNING;
import static nl.trifork.axondbbackupclient.cron.BackupTask.task;
@Service
@RequiredArgsConstructor
@Slf4j
public class BackupRunner {
private final Executor executor = newFixedThreadPool(8);
private final List<BackupTask> queue = new CopyOnWriteArrayList<>();
private final BackupService backupService;
private final BackupConfig config;
@Scheduled(cron = "${backup.cron}")
public void cron() {
BackupTask task = task(config.getJobs());
log.info("Adding cron task {}", task);
add(task);
}
public void add(BackupTask task) {
task.getJobs().forEach(j -> executor.execute(() -> runJob(j, task)));
queue.add(task);
}
private void runJob(BackupJob job, BackupTask task) {
Stopwatch sw = Stopwatch.createStarted();
MDC.put("traceId", task.getId());
task.getStatus().put(job.getName(), RUNNING);
log.info("Running job {}", job.getName());
try {
backupService.run(job);
task.getStatus().put(job.getName(), DONE);
log.info("Finished job {} for {} in {}", job.getName(), task, sw);
} catch (RuntimeException e) {
task.getStatus().put(job.getName(), ERROR);
log.error("Failed job {}", job.getName(), e);
} finally {
MDC.remove("traceId");
if (task.isFinished()) queue.remove(task);
}
}
}
package nl.trifork.axondbbackupclient.cron;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum BackupStatus {
SCHEDULLED(false),
RUNNING(false),
DONE(true),
ERROR(true);
private final boolean isFinished;
public boolean isFinished() {
return isFinished;
}
}
package nl.trifork.axondbbackupclient.cron;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import nl.trifork.axondbbackupclient.backup.BackupJob;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static nl.trifork.axondbbackupclient.cron.BackupStatus.SCHEDULLED;
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class BackupTask {
@EqualsAndHashCode.Include
private final String id;
private final Instant timestamp = Instant.now();
private final List<BackupJob> jobs;
private final Map<String, BackupStatus> status;
public static BackupTask task(List<BackupJob> jobs) {
String id = UUID.randomUUID().toString().substring(24);
Map<String, BackupStatus> statuses = new HashMap<>();
jobs.forEach(j -> statuses.put(j.getName(), SCHEDULLED));
return new BackupTask(id, jobs, statuses);
}
@Override
public String toString() {
return "BackupTask{" +
"id='" + id + '\'' +
", timestamp=" + timestamp +
", status=" + status +
'}';
}
public boolean isFinished() {
return status.values().stream().allMatch(BackupStatus::isFinished);
}
}
package nl.trifork.axondbbackupclient.backup_services;
package nl.trifork.axondbbackupclient.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
......@@ -9,26 +8,53 @@ import java.io.IOException;
import static org.apache.commons.io.FileUtils.copyFile;
@Slf4j
@Service
public class FileIoService {
public class FileUtils {
public void copy(File file, String destinationDir) {
public static String filenameFromPath(String path) {
int fileStart = path.lastIndexOf('/');
if (fileStart < 0) throw new IllegalArgumentException("Wrong path " + path);
return path.substring(fileStart + 1);
}
public static void ensureDir(String path) {
File dir = new File(path);
if (!dir.exists() || !dir.isDirectory()) {
if (!dir.mkdir()) {
throw new IllegalArgumentException("Failed to find/create dir: " + path);
}
}
}
public static void copy(File file, String destinationDir) {
copyWithTmp(file, destinationDir, false);
}
public static void copyWithOverride(File file, String destinationDir) {
copyWithTmp(file, destinationDir, true);
}
private static void copyWithTmp(File file, String destinationDir, boolean override) {
try {
String destinationFilePath = destinationDir + file.getName();
File destinationFile = new File(destinationFilePath);
if (destinationFile.exists()) {
log.warn("Destination file already present {}", destinationFile);
if (destinationFile.exists() && !override) {
return;
}
File tmpDestFile = new File(destinationFilePath + ".tmp");
copyFile(file, tmpDestFile);
if (destinationFile.exists() && override) {
log.warn("Overriding file {}", destinationFilePath);
destinationFile.delete();
}
tmpDestFile.renameTo(destinationFile);
} catch (IOException e) {
log.error("Failed to copy file {}", file, e);
}
}
}
package nl.trifork.axondbbackupclient.web;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import nl.trifork.axondbbackupclient.BackupConfig;
import nl.trifork.axondbbackupclient.cron.BackupRunner;
import nl.trifork.axondbbackupclient.cron.BackupTask;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import static nl.trifork.axondbbackupclient.cron.BackupTask.task;
@RestController
@RequiredArgsConstructor
@Slf4j
public class Controller {
private final BackupRunner runner;
private final BackupConfig config;
@PostMapping("/run")
public String runBackup() {
BackupTask task = task(config.getJobs());
log.info("Adding task {}", task);
runner.add(task);
return "Submitted";
}
}
spring:
application:
name: axondb-backup-client
main:
banner-mode: "off"
server:
port: ${SERVER_PORT:9080}
port: 9080
management:
endpoints:
web:
base-path: /manage
exposure:
include: "*"
include: health,loggers,info,metrics
backup:
fixedDelayMs: ${BACKUP_DELAY:60000}
initialDelayMs: ${BACKUP_INITIAL_DELAY:60000}
logging:
level:
root: ERROR
nl.trifork: TRACE
axondbHost: ${AXON_DB_HOST:http://127.0.0.1:8023/axondb}
axondbToken: ${AXON_DB_TOKEN:dummy-token}
backup:
# second, minute, hour, day, month, weekday
cron: "0 0 * * * *"
eventsUrl: /v1/backup/filenames?type=Event
snapshotsUrl: /v1/backup/filenames?type=Snapshot
dbDumpUrl: /v1/backup/createControlDbBackup
controlDbUrl: /v1/backup/createControlDbBackup
backupPath: ./backup/
axonPath: ./axon/
jobs:
- name: axondb1
axondbToken: dummy-token
axondbHost: http://127.0.0.1:8023/axondb
eventsPath: /events/default/
snapshotsPath: /events/default/
controlDbPath: /controldata/
eventsBackupPath: ${EVENTS_BACKUP_PATH:/backups/}
snapshotsBackupPath: ${SNAPSHOTS_BACKUP_PATH:/backups/}
dbDumpBackupPath: ${CONTROL_DB_BACKUP_PATH:/backups/}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{dd-MM-yyyy HH:mm:ss.SSS} %highlight(%-5level) %X{traceId} %-25.25class{0} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>
\ No newline at end of file
......@@ -7,7 +7,6 @@ import org.axonframework.config.Configuration;
import org.axonframework.eventhandling.EventBus;
import org.axonframework.eventhandling.GenericEventMessage;
import org.axonframework.serialization.xml.XStreamSerializer;
import org.junit.Ignore;
import org.junit.Test;
import java.util.stream.LongStream;
......@@ -16,15 +15,16 @@ import static io.axoniq.axondb.client.AxonDBConfiguration.newBuilder;
import static java.util.UUID.randomUUID;
import static org.axonframework.config.DefaultConfigurer.defaultConfiguration;
@Ignore("for manual usage")
//@Ignore("for manual usage")
public class AxonDbLoadTest {
EventBus eventBus = eventBus();
@Test
public void should_publish_events() {
LongStream.range(0, 300_000L)
LongStream.range(0, 10_000L)
.parallel()
.peek(System.out::println)
.mapToObj(i -> new SomeEvent())
.map(GenericEventMessage::asEventMessage)
.forEach(eventBus::publish);
......@@ -41,6 +41,7 @@ public class AxonDbLoadTest {
private static class SomeEvent {
@TargetAggregateIdentifier String targetId = randomUUID().toString();
private final String payload = "Event Message";
}
......
package nl.trifork.axondbbackupclient;
import nl.trifork.axondbbackupclient.axondb_client.AxonDbRestClient;
import nl.trifork.axondbbackupclient.backup_services.ControlDBBackupService;
import nl.trifork.axondbbackupclient.backup_services.FileIoService;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class ControlDBBackupServiceTest {
@Rule
public TemporaryFolder tmp = new TemporaryFolder();
BackupConfig config = new BackupConfig();
AxonDbRestClient client = mock(AxonDbRestClient.class);
FileIoService fileService = mock(FileIoService.class);
ControlDBBackupService subj = new ControlDBBackupService(config, client, fileService);
@Test
public void should_create_and_copy_db_dump() throws IOException {
File dumpFile = tmp.newFile("control1234.db");
File dbBackup = tmp.newFolder("dbBackup");
when(client.createControlDbDump()).thenReturn(dumpFile.getAbsolutePath());
config.setDbDumpBackupPath(dbBackup.getAbsolutePath());
//when
subj.backup();
//then
verify(fileService).copy(dumpFile, config.getDbDumpBackupPath());
assertThat(dumpFile.exists()).isFalse();
}
}
\ No newline at end of file