-
Notifications
You must be signed in to change notification settings - Fork 0
Create database backup method #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Moltensnor
wants to merge
2
commits into
main
Choose a base branch
from
create-database-backup-method
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
254 changes: 254 additions & 0 deletions
254
src/main/kotlin/com/ex_dock/ex_dock/database/service/DatabaseBackupVerticle.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,254 @@ | ||
| package com.ex_dock.ex_dock.database.service | ||
|
|
||
| import com.ex_dock.ex_dock.database.connection.Connection | ||
| import io.vertx.core.AbstractVerticle | ||
| import io.vertx.core.eventbus.EventBus | ||
| import io.vertx.sqlclient.Pool | ||
| import java.io.BufferedReader | ||
| import java.io.File | ||
| import java.io.IOException | ||
| import java.io.InputStreamReader | ||
| import java.time.LocalTime | ||
| import java.time.ZoneId | ||
| import java.time.ZonedDateTime | ||
| import java.time.format.DateTimeFormatter | ||
| import java.time.temporal.ChronoField | ||
| import java.time.temporal.ChronoUnit | ||
| import java.util.* | ||
|
|
||
| /** | ||
| * Verticle for all events connected to database backups | ||
| */ | ||
| class DatabaseBackupVerticle: AbstractVerticle() { | ||
| private lateinit var client: Pool | ||
| private lateinit var eventBus: EventBus | ||
| private lateinit var frequency: String | ||
| private lateinit var time: String | ||
| private lateinit var dbUser: String | ||
| private lateinit var dbPassword: String | ||
| private var savedBackups = 1 | ||
|
|
||
| /** | ||
| * Initialize variables from properties and start the eventbus and scheduler | ||
| */ | ||
| override fun start() { | ||
| client = Connection().getConnection(vertx) | ||
| eventBus = vertx.eventBus() | ||
|
|
||
| //Read the variables from the properties file | ||
| try { | ||
| val props: Properties = javaClass.classLoader.getResourceAsStream("secret.properties").use { | ||
| Properties().apply { load(it) } | ||
| } | ||
|
|
||
| frequency = props.getProperty("BACKUP_FREQUENCY") | ||
| time = props.getProperty("BACKUP_TIME") | ||
| savedBackups = props.getProperty("SAVED_BACKUPS").toInt() | ||
| dbUser = props.getProperty("DATABASE_USERNAME") | ||
| dbPassword = props.getProperty("DATABASE_PASSWORD") | ||
|
|
||
| } catch (e: Exception) { | ||
| println(e.message) | ||
| try { | ||
| // Try to load the docker values when run in GitHub actions | ||
| val isDocker: Boolean = !System.getenv("GITHUB_RUN_NUMBER").isNullOrEmpty() | ||
| if (isDocker) { | ||
| frequency = "daily" | ||
| time = "00:00" | ||
| savedBackups = 1 | ||
| } else { | ||
| error("Could not load the Properties file!") | ||
| } | ||
| } catch (e: Exception) { | ||
| println(e.message) | ||
| error("Could not read the Properties file!") | ||
| } | ||
| } | ||
|
|
||
| backupWithEventBusRequest() | ||
| scheduler() | ||
| } | ||
|
|
||
| /** | ||
| * Makes a full backup of the current state of the database | ||
| */ | ||
| private fun backupDatabaseData() { | ||
| organizeBackups() | ||
| val zdt: ZonedDateTime = ZonedDateTime.now(ZoneId.systemDefault()) | ||
| val zdtTruncatedHour: ZonedDateTime = zdt.truncatedTo(ChronoUnit.HOURS) | ||
| val f: DateTimeFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy HH", Locale.getDefault()) | ||
| val timeString = zdtTruncatedHour.format(f) | ||
| .replace(" ", "_") | ||
| .replace("/", "_") + | ||
| ".sql" | ||
|
|
||
| val backupDirectory = System.getProperty("user.dir") + "\\database\\backup\\backup_$timeString" | ||
| val processBuilder = ProcessBuilder( | ||
| "pg_dump", | ||
| "--host", "localhost", | ||
| "--port", "8890", | ||
| "--file", backupDirectory, | ||
| "--blobs", | ||
| "--verbose", | ||
| "--username", dbUser, | ||
| "--no-password", | ||
| "ex-dock" | ||
| ) | ||
|
|
||
| try { | ||
| val env = processBuilder.environment() | ||
| env["PGPASSWORD"] = dbPassword | ||
| val p: Process = processBuilder.start() | ||
| val bufferedReader = BufferedReader(InputStreamReader(p.errorStream)) | ||
| var line = bufferedReader.readLine() | ||
| while (line!= null) { | ||
| println(line) | ||
| line = bufferedReader.readLine() | ||
| } | ||
| } catch (e: IOException) { | ||
| e.printStackTrace() | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Gets the request for a backup from the eventbus | ||
| */ | ||
| private fun backupWithEventBusRequest() { | ||
| eventBus.consumer<String>("process.service.backup").handler { message -> | ||
| backupDatabaseData() | ||
| message.reply("Completed backup!") | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Creates a scheduler for making backups at specified intervals | ||
| */ | ||
| private fun scheduler() { | ||
| val frequencyInt: Long = getFrequencyTimeNumber(frequency) | ||
| val timeInt: Long = getTimeNumber(time) | ||
| val currentTime: Long = LocalTime.now(ZoneId.systemDefault())[ChronoField.MILLI_OF_DAY].toLong() | ||
| var waitingTime: Long = timeInt - currentTime | ||
|
|
||
| if (waitingTime < 0) { | ||
| val tempTimeInt = getTimeNumber("23:59") | ||
| waitingTime = (tempTimeInt - currentTime) + timeInt | ||
| } | ||
|
|
||
| vertx.setTimer(waitingTime) { | ||
| backupDatabaseData() | ||
|
|
||
| // Sets the given interval | ||
| vertx.setPeriodic(frequencyInt*1000L) { | ||
| backupDatabaseData() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Converts the given frequency to a usable number for the scheduler | ||
| */ | ||
| private fun getFrequencyTimeNumber(frequency: String): Long { | ||
| return when (frequency) { | ||
| "daily" -> 1*24*60*60 | ||
| "weekly" -> 7*24*60*60 | ||
| "monthly" -> 30*24*60*60 | ||
| "hourly" -> 60*60 | ||
| else -> 1 | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Gets the time in milliseconds for the wanted backup time | ||
| */ | ||
| private fun getTimeNumber(time: String): Long { | ||
| val parts = time.split(":") | ||
| return (parts[0].toInt() * 60 * 60 + parts[1].toInt() * 60) * 1000L | ||
| } | ||
|
|
||
| /** | ||
| * Removes the oldest backup if the number of backups is greater than the maximum number of backups allowed | ||
| */ | ||
| private fun organizeBackups() { | ||
| val backupDirectory = System.getProperty("user.dir") + "\\database\\backup" | ||
| val folder = File(backupDirectory) | ||
| val listOfFiles = folder.listFiles() | ||
| val fileDateList: MutableList<String> = emptyList<String>().toMutableList() | ||
| var oldestDate = "" | ||
|
|
||
| // Skip organizing when the number of backups is lower than the maximum number of backups allowed | ||
| if (listOfFiles!!.size >= savedBackups) { | ||
| for (f in listOfFiles) { | ||
| val fileDateString = f.name | ||
| .replace("backup_", "") | ||
| .replace(".sql", "") | ||
| fileDateList.add(fileDateString) | ||
| } | ||
|
|
||
| for (dateString in fileDateList) { | ||
| oldestDate = if (oldestDate != "") { | ||
| val dateParts = dateString.split("_") | ||
| val oldestDateParts = oldestDate.split("_") | ||
|
|
||
| checkIfOlderFile( | ||
| dateParts, | ||
| oldestDateParts, | ||
| dateString, | ||
| oldestDate) | ||
| } else { | ||
| dateString | ||
| } | ||
| } | ||
|
|
||
| val oldestBackup = File("$backupDirectory\\backup_$oldestDate.sql") | ||
|
|
||
| if (oldestBackup.delete()) { | ||
| println("Deleted oldest backup from: $oldestDate") | ||
| } else { | ||
| println("Could not delete oldest backup from: $oldestDate") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Checks the backup name and returns the oldest backup date | ||
| */ | ||
| private fun checkIfOlderFile(dateParts: List<String>, | ||
| oldestDateParts: List<String>, | ||
| dateString: String, | ||
| oldestDate: String): String { | ||
| // Check if the Year is older | ||
| if (dateParts[2].toInt() < oldestDateParts[2].toInt()) { | ||
| return dateString | ||
|
|
||
| // Check if the Year is the same | ||
| } else if (dateParts[2].toInt() == oldestDateParts[2].toInt()) { | ||
|
|
||
| // Check if the Month is older | ||
| if (dateParts[1].toInt() < oldestDateParts[1].toInt()) { | ||
| return dateString | ||
|
|
||
| // Check if the Month is the same | ||
| } else if (dateParts[1].toInt() == oldestDateParts[1].toInt()) { | ||
|
|
||
| // Check if the Day is older | ||
| if (dateParts[0].toInt() < oldestDateParts[0].toInt()) { | ||
| return dateString | ||
|
|
||
| // Check if the Day is the same | ||
| } else if (dateParts[0].toInt() == oldestDateParts[0].toInt()) { | ||
|
|
||
| // Check if the Hour is older | ||
| if (dateParts[3].toInt() < oldestDateParts[3].toInt()) { | ||
| return dateString | ||
|
|
||
| // Check if the Hour is the same | ||
| } else if (dateParts[3].toInt() == oldestDateParts[3].toInt()) { | ||
| return dateString | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return oldestDate | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.