Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created September 10, 2020 11:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bennadel/ae361f22e93a0ea2ba1110660c6585ab to your computer and use it in GitHub Desktop.
Save bennadel/ae361f22e93a0ea2ba1110660c6585ab to your computer and use it in GitHub Desktop.
Deleting Temporary Upload Files In Our K8 Operational Readiness Probe In Lucee CFML 5.3.6.61
component
output = false
hint = "I help log JVM system data for the health-probe."
{
// ... truncated code ...
// ---
// PUBLIC METHODS.
// ---
/**
* I log the operations probe / health check for the server. The return value
* indicates whether or not the probe should be kept online (true = online); or, if it
* should be failed out of rotation.
*/
public boolean function logProbeRequest() {
// NOTE: All of the runSafelyAsync() calls are spawned in a CFThread and
// synchronized internally such that overlapping health-probe calls will not
// trigger competing system measurements.
// ... truncated code ...
if ( shouldMeasureTempDirectory() ) {
runSafelyAsync( "measureTempDirectory" );
}
// CAUTION: This is a "hack" that we're putting in place because Lucee's native
// background-task seems to be failing and we don't know why. As such, we're
// going to see if we can fill in the gap while we continue to debug the issue.
if ( shouldDeleteOldTempFiles() ) {
runSafelyAsync( "deleteOldTempFiles" );
}
// ... truncated code ...
}
// ---
// PRIVATE METHODS.
// ---
/**
* I delete temporary upload files from the server's temp directory.
*/
private void function deleteOldTempFiles() {
var fileCount = tempDirectoryHelper.deleteOldTempFiles(
filter = "tmp-*.upload",
ageInMinutes = 15
);
datadog.increment(
key = getMetricName( "temp-directory.files-deleted" ),
magnitude = fileCount,
tags = tags
);
}
// ... truncated code ...
/**
* I record the current Lucee CFML temp-directory usage for this pod.
*/
private void function measureTempDirectory() {
var directoryStats = tempDirectoryHelper.getStats();
datadog.gauge(
key = getMetricName( "temp-directory.total-size" ),
value = directoryStats.size,
tags = tags
);
datadog.gauge(
key = getMetricName( "temp-directory.total-count" ),
value = directoryStats.count,
tags = tags
);
}
// ... truncated code ...
/**
* I execute and synchronize access to the given measurement method. This ensures
* that the act of running the given method doesn't, in and of itself, cause the
* health probe to fail.
*/
private void function runSafelyAsync( required string methodName ) {
try {
thread
name = "probe.#methodName#-thread"
action = "run"
methodName = methodName
{
try {
// Now that this method is being invoked asynchronously to the probe
// request, there's a chance that this measurement may overlap with
// a subsequent measurement. As such, let's synchronize the
// measurement operation(s) so that only one-of-a-type can be
// executing at a time.
lock
name = "probe.#methodName#-lock"
type = "exclusive"
timeout = 1
throwOnTimeout = false
{
invoke( variables, methodName );
} // END: Lock.
} catch ( any error ) {
debugLogger.error( error, "Error calling [#methodName#()] in the operations probe." );
}
} // END: Thread.
} catch ( any error ) {
debugLogger.error( error, "Error spawning async thread in the operations probe." );
}
}
// ... truncated code ...
/**
* I check to see if the current machine should attempt to delete old files in the
* server's temp-directory.
*/
private boolean function shouldDeleteOldTempFiles() {
return( featureFlagService.getFeatureByUserID( hostName, "OPERATIONS--cfprojectsapi--delete-old-temp-files" ) );
}
// ... truncated code ...
/**
* I check to see if the current machine should be measuring temp-directory stats as
* part of the metrics that is emits on each health-check.
*/
private boolean function shouldMeasureTempDirectory() {
return( featureFlagService.getFeatureByUserID( hostName, "OPERATIONS--cfprojectsapi--measure-temp-directory-in-health-check" ) );
}
// ... truncated code ...
}
component
output = false
hint = "I provide helper methods for Lucee's temp-directory."
{
/**
* I initialize the temp-directory helper using the given directory as the target
* directory.
*
* @tempDirectoryPath I am the temp-directory for the server.
*/
public void function init( string tempDirectoryPath = getTempDirectory() ) {
variables.tempDirectoryPath = arguments.tempDirectoryPath;
// Ensure that the temp-directory ends with a slash - we'll need this when we
// programmatically generate file-paths later on.
if ( ! variables.tempDirectoryPath.reFind( "[\\/]$" ) ) {
variables.tempDirectoryPath &= "/";
}
}
// ---
// PUBLIC METHODS.
// ---
/**
* I delete files from the temp-directory that match the given filter and are older
* than the given age in minutes. I return the number of files deleted.
*
* CAUTION: If a File IO error is thrown, it halts the current process and the error
* bubbles up to the calling context.
*
* @filter I am the filter to pass to directoryList().
* @ageInMinutes I am the positive age in minutes after which a file will be deleted.
*/
public numeric function deleteOldTempFiles(
required string filter,
required numeric ageInMinutes
) {
var filesQuery = directoryList(
path = tempDirectoryPath,
listInfo = "query",
filter = filter,
sort = "dateLastModified ASC",
type = "file"
);
var cutoffAt = now().add( "n", -ageInMinutes );
var deletedCount = 0;
loop query = filesQuery {
if ( filesQuery.dateLastModified < cutoffAt ) {
fileDelete( tempDirectoryPath & filesQuery.name );
deletedCount++;
} else {
// Since we sorted the file-query by date-descending, we know that once
// we hit a file that is relatively "new", we've exhausted all of the old
// files. Everything after this is going to be "new" and can be ignored.
break;
}
}
return( deletedCount );
}
/**
* I return the size and count stats for the temp-directory.
*
* CAUTION: Any sub-directories that are part of the temp-directory will be counted
* as a single item of size zero bytes. Right now, I think our problem is related to
* temporary files, but we can re-evaluate later.
*/
public struct function getStats() {
var totalCount = 0;
var totalSize = 0;
if ( directoryExists( tempDirectoryPath ) ) {
var filesQuery = directoryList(
path = tempDirectoryPath,
listInfo = "query"
);
totalCount = filesQuery.recordCount;
loop query = filesQuery {
totalSize += size;
}
}
return({
count: totalCount,
size: totalSize
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment