Skip to content

Commit

Permalink
GH-1975 Retry Files.put in FileSystemStorageProvider (Fix #1975)
Browse files Browse the repository at this point in the history
  • Loading branch information
dzikoysk committed Nov 10, 2023
1 parent e185d32 commit f199b4e
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ internal class FileSystemStorageProviderIntegrationTest : StorageProviderIntegra
val storageFacade = StorageFacade()

super.storageProvider = storageFacade.createStorageProvider(
journalist = logger,
failureFacade = failureFacade,
workingDirectory = rootDirectory.toPath(),
repository = "test-storage",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ internal class S3StorageProviderIntegrationTest : StorageProviderIntegrationTest
val storageFacade = StorageFacade()

this.storageProvider = storageFacade.createStorageProvider(
journalist = logger,
failureFacade = failureFacade,
workingDirectory = rootDirectory.toPath(),
repository = "test-repository",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.reposilite.maven

import com.reposilite.auth.AuthenticationFacade
import com.reposilite.journalist.Journalist
import com.reposilite.maven.application.MirroredRepositorySettings
import com.reposilite.maven.application.RepositorySettings
import com.reposilite.shared.http.RemoteClientProvider
Expand All @@ -28,6 +29,7 @@ import java.nio.file.Paths
import java.util.UUID

internal class RepositoryFactory(
private val journalist: Journalist,
private val workingDirectory: Path,
private val authenticationFacade: AuthenticationFacade,
private val remoteClientProvider: RemoteClientProvider,
Expand All @@ -49,6 +51,7 @@ internal class RepositoryFactory(
storageProvider =
storageFacade
.createStorageProvider(
journalist = journalist,
failureFacade = failureFacade,
workingDirectory = workingDirectory.resolve(repositoriesDirectory),
repository = repositoryName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ internal class RepositoryProvider(

private fun createRepositories(repositoriesConfiguration: List<RepositorySettings>): Map<String, Repository> {
val factory = RepositoryFactory(
journalist = journalist,
workingDirectory = workingDirectory,
authenticationFacade = authenticationFacade,
remoteClientProvider = remoteClientProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.reposilite.storage

import com.reposilite.journalist.Journalist
import com.reposilite.plugin.api.Facade
import com.reposilite.status.FailureFacade
import java.nio.file.Path
Expand All @@ -28,7 +29,19 @@ class StorageFacade : Facade {
.associateBy { it.type }
.mapValues { (_, factory) -> factory as StorageProviderFactory<*, StorageProviderSettings> }

fun createStorageProvider(failureFacade: FailureFacade, workingDirectory: Path, repository: String, storageSettings: StorageProviderSettings): StorageProvider? =
storageProviderFactories[storageSettings.type]?.create(failureFacade, workingDirectory, repository, storageSettings)
fun createStorageProvider(
journalist: Journalist,
failureFacade: FailureFacade,
workingDirectory: Path,
repository: String,
storageSettings: StorageProviderSettings
): StorageProvider? =
storageProviderFactories[storageSettings.type]?.create(
journalist = journalist,
failureFacade = failureFacade,
workingDirectory = workingDirectory,
repositoryName = repository,
settings = storageSettings
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.reposilite.storage

import com.reposilite.journalist.Journalist
import com.reposilite.status.FailureFacade
import java.nio.file.Path

Expand All @@ -24,6 +25,12 @@ interface StorageProviderFactory<PROVIDER : StorageProvider, SETTINGS : StorageP
val type: String
val settingsType: Class<SETTINGS>

fun create(failureFacade: FailureFacade, workingDirectory: Path, repositoryName: String, settings: SETTINGS): PROVIDER
fun create(
journalist: Journalist,
failureFacade: FailureFacade,
workingDirectory: Path,
repositoryName: String,
settings: SETTINGS
): PROVIDER

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.reposilite.storage.filesystem

import com.reposilite.journalist.Journalist
import com.reposilite.shared.ErrorResponse
import com.reposilite.shared.toErrorResponse
import io.javalin.http.HttpStatus.INSUFFICIENT_STORAGE
Expand All @@ -27,7 +28,11 @@ import java.nio.file.Path
* @param rootDirectory root directory of storage space
* @param maxSize the largest amount of storage available for use, in bytes
*/
internal class FixedQuota(rootDirectory: Path, private val maxSize: Long) : FileSystemStorageProvider(rootDirectory) {
internal class FixedQuota(
journalist: Journalist,
rootDirectory: Path,
private val maxSize: Long
) : FileSystemStorageProvider(journalist, rootDirectory) {

init {
if (maxSize <= 0) {
Expand All @@ -47,9 +52,10 @@ internal class FixedQuota(rootDirectory: Path, private val maxSize: Long) : File
* @param maxPercentage the maximum percentage of the disk available for use
*/
internal class PercentageQuota(
journalist: Journalist,
rootDirectory: Path,
private val maxPercentage: Double
) : FileSystemStorageProvider(rootDirectory) {
) : FileSystemStorageProvider(journalist, rootDirectory) {

init {
if (maxPercentage > 1 || maxPercentage <= 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.reposilite.storage.filesystem

import com.reposilite.journalist.Journalist
import com.reposilite.shared.ErrorResponse
import com.reposilite.shared.badRequest
import com.reposilite.shared.notFound
Expand Down Expand Up @@ -43,15 +44,18 @@ import java.io.File
import java.io.IOException
import java.io.InputStream
import java.nio.file.Files
import java.nio.file.NoSuchFileException
import java.nio.file.Path
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
import java.nio.file.attribute.FileTime
import kotlin.io.path.absolutePathString
import kotlin.streams.asSequence

/**
* @param rootDirectory root directory of storage space
*/
abstract class FileSystemStorageProvider protected constructor(
val journalist: Journalist,
val rootDirectory: Path
) : StorageProvider {

Expand All @@ -78,8 +82,16 @@ abstract class FileSystemStorageProvider protected constructor(
data.copyTo(destination)
}

Files.move(temporaryFile.toPath(), file, REPLACE_EXISTING)
Unit
do {
try {
Files.move(temporaryFile.toPath(), file, REPLACE_EXISTING)
} catch (e: NoSuchFileException) {
// Concurrent Files.move calls may throw
// ~ https://github.com/dzikoysk/reposilite/issues/1975
journalist.logger.debug("[FS] Cannot move file ${temporaryFile.absolutePath} to ${file.absolutePathString()}, retrying...")
Thread.sleep(100) // probably good enough for now
}
} while (Files.exists(temporaryFile.toPath()))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.reposilite.storage.filesystem

import com.reposilite.journalist.Journalist
import com.reposilite.status.FailureFacade
import com.reposilite.storage.StorageProviderFactory
import java.nio.file.Files
Expand All @@ -34,26 +35,26 @@ class FileSystemStorageProviderFactory : StorageProviderFactory<FileSystemStorag
* @param rootDirectory root directory of storage space
* @param quota quota to use as % or in bytes
*/
fun of(rootDirectory: Path, quota: String): FileSystemStorageProvider =
fun of(journalist: Journalist, rootDirectory: Path, quota: String): FileSystemStorageProvider =
if (quota.endsWith("%")) {
of(rootDirectory, quota.substring(0, quota.length - 1).toInt() / 100.0)
of(journalist, rootDirectory, quota.substring(0, quota.length - 1).toInt() / 100.0)
} else {
of(rootDirectory, displaySizeToBytesCount(quota))
of(journalist, rootDirectory, displaySizeToBytesCount(quota))
}

/**
* @param rootDirectory root directory of storage space
* @param maxSize the largest amount of storage available for use, in bytes
*/
fun of(rootDirectory: Path, maxSize: Long): FileSystemStorageProvider =
FixedQuota(rootDirectory, maxSize)
fun of(journalist: Journalist, rootDirectory: Path, maxSize: Long): FileSystemStorageProvider =
FixedQuota(journalist, rootDirectory, maxSize)

/**
* @param rootDirectory root directory of storage space
* @param maxPercentage the maximum percentage of the disk available for use
*/
fun of(rootDirectory: Path, maxPercentage: Double): FileSystemStorageProvider =
PercentageQuota(rootDirectory, maxPercentage)
fun of(journalist: Journalist, rootDirectory: Path, maxPercentage: Double): FileSystemStorageProvider =
PercentageQuota(journalist, rootDirectory, maxPercentage)

private fun displaySizeToBytesCount(displaySize: String): Long {
val match = DISPLAY_SIZE_PATTERN.matcher(displaySize)
Expand All @@ -74,6 +75,7 @@ class FileSystemStorageProviderFactory : StorageProviderFactory<FileSystemStorag
}

override fun create(
journalist: Journalist,
failureFacade: FailureFacade,
workingDirectory: Path,
repositoryName: String,
Expand All @@ -86,7 +88,7 @@ class FileSystemStorageProviderFactory : StorageProviderFactory<FileSystemStorag
workingDirectory.resolve(settings.mount)

Files.createDirectories(repositoryDirectory)
return of(repositoryDirectory, settings.quota)
return of(journalist, repositoryDirectory, settings.quota)
}

override val settingsType: Class<FileSystemStorageProviderSettings> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,27 @@

package com.reposilite.storage.s3

import com.reposilite.journalist.Journalist
import com.reposilite.status.FailureFacade
import com.reposilite.storage.StorageProviderFactory
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
import software.amazon.awssdk.core.exception.SdkClientException
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.S3Configuration
import software.amazon.awssdk.services.s3.model.S3Exception
import java.net.URI
import java.nio.file.Path

class S3StorageProviderFactory : StorageProviderFactory<S3StorageProvider, S3StorageProviderSettings> {

override fun create(failureFacade: FailureFacade, workingDirectory: Path, repositoryName: String, settings: S3StorageProviderSettings): S3StorageProvider {
override fun create(
journalist: Journalist,
failureFacade: FailureFacade,
workingDirectory: Path,
repositoryName: String,
settings: S3StorageProviderSettings
): S3StorageProvider {
val client = S3Client.builder()

val pathStyleAccessEnabled = System.getProperty("reposilite.s3.pathStyleAccessEnabled") == "true"

if (pathStyleAccessEnabled) {
Expand Down

0 comments on commit f199b4e

Please sign in to comment.