Skip to content
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

Package Cache lock cleanup #1745

Merged
merged 14 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.*;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import lombok.Getter;
import lombok.Setter;
Expand Down Expand Up @@ -77,12 +78,13 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple

private final FilesystemPackageCacheManagerLocks locks;

private final FilesystemPackageCacheManagerLocks.LockParameters lockParameters;

// When running in testing mode, some packages are provided from the test case repository rather than by the normal means
// the PackageProvider is responsible for this. if no package provider is defined, or it declines to handle the package,
// then the normal means will be used
public interface IPackageProvider {
boolean handlesPackage(String id, String version);

InputStreamWithSrc provide(String id, String version) throws IOException;
}

Expand All @@ -92,6 +94,7 @@ public interface IPackageProvider {
public static final String PACKAGE_VERSION_REGEX_OPT = "^[A-Za-z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+(\\#[A-Za-z0-9\\-\\_]+(\\.[A-Za-z0-9\\-\\_]+)*)?$";
private static final Logger ourLog = LoggerFactory.getLogger(FilesystemPackageCacheManager.class);
private static final String CACHE_VERSION = "3"; // second version - see wiki page

@Nonnull
private final File cacheFolder;

Expand All @@ -100,6 +103,7 @@ public interface IPackageProvider {
private final Map<String, String> ciList = new HashMap<>();
private JsonArray buildInfo;
private boolean suppressErrors;

@Setter
@Getter
private boolean minimalMemory;
Expand All @@ -113,9 +117,20 @@ public static class Builder {
@Getter
private final List<PackageServer> packageServers;

@With
@Getter
private final FilesystemPackageCacheManagerLocks.LockParameters lockParameters;

public Builder() throws IOException {
this.cacheFolder = getUserCacheFolder();
this.packageServers = getPackageServersFromFHIRSettings();
this.lockParameters = null;
}

private Builder(File cacheFolder, List<PackageServer> packageServers, FilesystemPackageCacheManagerLocks.LockParameters lockParameters) {
this.cacheFolder = cacheFolder;
this.packageServers = packageServers;
this.lockParameters = lockParameters;
}

private File getUserCacheFolder() throws IOException {
Expand Down Expand Up @@ -143,17 +158,12 @@ protected List<PackageServer> getConfiguredServers() {
return PackageServer.getConfiguredServers();
}

private Builder(File cacheFolder, List<PackageServer> packageServers) {
this.cacheFolder = cacheFolder;
this.packageServers = packageServers;
}

public Builder withCacheFolder(String cacheFolderPath) throws IOException {
File cacheFolder = ManagedFileAccess.file(cacheFolderPath);
if (!cacheFolder.exists()) {
throw new FHIRException("The folder '" + cacheFolder + "' could not be found");
}
return new Builder(cacheFolder, this.packageServers);
return new Builder(cacheFolder, this.packageServers, this.lockParameters);
}

public Builder withSystemCacheFolder() throws IOException {
Expand All @@ -163,32 +173,33 @@ public Builder withSystemCacheFolder() throws IOException {
} else {
systemCacheFolder = ManagedFileAccess.file(Utilities.path("/var", "lib", ".fhir", "packages"));
}
return new Builder(systemCacheFolder, this.packageServers);
return new Builder(systemCacheFolder, this.packageServers, this.lockParameters);
}

public Builder withTestingCacheFolder() throws IOException {
return new Builder(ManagedFileAccess.file(Utilities.path("[tmp]", ".fhir", "packages")), this.packageServers);
return new Builder(ManagedFileAccess.file(Utilities.path("[tmp]", ".fhir", "packages")), this.packageServers, this.lockParameters);
}

public FilesystemPackageCacheManager build() throws IOException {
return new FilesystemPackageCacheManager(cacheFolder, packageServers);
final FilesystemPackageCacheManagerLocks locks;
try {
locks = FilesystemPackageCacheManagerLocks.getFilesystemPackageCacheManagerLocks(cacheFolder);
} catch (RuntimeException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
throw e;
}
}
return new FilesystemPackageCacheManager(cacheFolder, packageServers, locks, lockParameters);
}
}

private FilesystemPackageCacheManager(@Nonnull File cacheFolder, @Nonnull List<PackageServer> packageServers) throws IOException {
private FilesystemPackageCacheManager(@Nonnull File cacheFolder, @Nonnull List<PackageServer> packageServers, @Nonnull FilesystemPackageCacheManagerLocks locks, @Nullable FilesystemPackageCacheManagerLocks.LockParameters lockParameters) throws IOException {
super(packageServers);
this.cacheFolder = cacheFolder;

try {
this.locks = FilesystemPackageCacheManagerLocks.getFilesystemPackageCacheManagerLocks(cacheFolder);
} catch (RuntimeException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
} else {
throw e;
}
}

this.locks = locks;
this.lockParameters = lockParameters;
prepareCacheFolder();
}

Expand Down Expand Up @@ -218,11 +229,35 @@ protected void prepareCacheFolder() throws IOException {
createIniFile();
}
deleteOldTempDirectories();
cleanUpCorruptPackages();
}
return null;
});
}

/*
Look for .lock files that are not actively held by a process. If found, delete the lock file, and the package
referenced.
*/
protected void cleanUpCorruptPackages() throws IOException {
for (File file : Objects.requireNonNull(cacheFolder.listFiles())) {
if (file.getName().endsWith(".lock")) {
if (locks.getCacheLock().canLockFileBeHeldByThisProcess(file)) {
String packageDirectoryName = file.getName().substring(0, file.getName().length() - 5);
log("Detected potential incomplete package installed in cache: " + packageDirectoryName + ". Attempting to delete");

File packageDirectory = ManagedFileAccess.file(Utilities.path(cacheFolder, packageDirectoryName));
if (packageDirectory.exists()) {
Utilities.clearDirectory(packageDirectory.getAbsolutePath());
packageDirectory.delete();
}
file.delete();
log("Deleted potential incomplete package: " + packageDirectoryName);
}
}
}
}

private boolean iniFileExists() throws IOException {
String iniPath = getPackagesIniPath();
File iniFile = ManagedFileAccess.file(iniPath);
Expand Down Expand Up @@ -421,7 +456,7 @@ public void removePackage(String id, String version) throws IOException {
}

return null;
});
}, lockParameters);
}

/**
Expand Down Expand Up @@ -465,7 +500,7 @@ public NpmPackage loadPackageFromCacheOnly(String id, String version) throws IOE
return null;
}
return loadPackageInfo(path);
});
}, lockParameters);
if (foundPackage != null) {
if (foundPackage.isIndexed()){
return foundPackage;
Expand All @@ -488,7 +523,7 @@ public NpmPackage loadPackageFromCacheOnly(String id, String version) throws IOE
String path = Utilities.path(cacheFolder, foundPackageFolder);
output.checkIndexed(path);
return output;
});
}, lockParameters);
}
}
}
Expand Down Expand Up @@ -589,7 +624,7 @@ public NpmPackage addPackageToCache(final String id, final String version, final
throw e;
}
return npmPackage;
});
}, lockParameters);
}

private void log(String s) {
Expand Down
Loading
Loading