Skip to content
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
2 changes: 1 addition & 1 deletion docs/designs/PowerShell-AzF-Overall-Design.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ A snapshot is considered _acceptable_ if it contains any version _allowed_ by th

However, if the latest snapshot is _not acceptable_ (i.e. it does not contain module versions required by the manifest), the worker starts installing the dependencies into a new snapshot, and all the subsequent function invocation requests are blocked, waiting for the new snapshot installation to complete.

When a snapshot installation starts, the dependencies are first installed into a folder with a name following a special pattern (`*.i`), so that this snapshot is not picked up by any worker prematurely, before the installation is complete. After _successful_ completion, the snapshot is _atomically promoted_ by renaming the folder to follow a different pattern (`*.r`), which indicates to other workers that this snapshot is ready to use. If the installation fails or cannot complete for any reason (for example, the worker restarts, crashes, or gets decommissioned), the folder stays in the installing state until removed.
When a snapshot installation starts, the dependencies are first installed into a folder with a name following a special pattern (`*i`), so that this snapshot is not picked up by any worker prematurely, before the installation is complete. After _successful_ completion, the snapshot is _atomically promoted_ by renaming the folder to follow a different pattern (`*r`), which indicates to other workers that this snapshot is ready to use. If the installation fails or cannot complete for any reason (for example, the worker restarts, crashes, or gets decommissioned), the folder stays in the installing state until removed.

Incomplete and old snapshots that are no longer in use are periodically removed from the file storage. In order to allow detecting unused snapshots, each PowerShell worker keeps "touching" a file named `.used` at the root of the used snapshot folder every `PSWorkerHeartbeatPeriodMinutes` minutes. Before and after installing any new snapshot, every PowerShell worker looks for unused snapshots by checking the folder creation time and the `.used` file modification time. If both these time values are older than (`PSWorkerHeartbeatPeriodMinutes` + `PSWorkerOldSnapshotHeartbeatMarginMinutes`) minutes, the snapshot is considered unused, so the PowerShell worker removes it. The latest `PSWorkerMinNumberOfSnapshotsToKeep` snapshots will never be removed, regardless of usage. (`PSWorkerHeartbeatPeriodMinutes`, `PSWorkerOldSnapshotHeartbeatMarginMinutes`, and `PSWorkerMinNumberOfSnapshotsToKeep` are environment variables configurable via Application Settings of a Function App.)

Expand Down
14 changes: 7 additions & 7 deletions src/DependencyManagement/DependencySnapshotFolderNameTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement

internal static class DependencySnapshotFolderNameTools
{
private const string InstallingPostfix = ".i";
private const string InstallingPostfix = "i";

public const string InstalledPostfix = ".r";
public const string InstalledPostfix = "r";

public const string InstalledPattern = "*" + InstalledPostfix;

private const string AccessMarkerFileName = ".used";

public static string CreateUniqueName()
{
var uniqueBase = DateTime.UtcNow.ToString("yyyyMMdd-HHmmss.ffffff");
var uniqueBase = DateTime.UtcNow.ToString("yyMMddHHmmssfff");
return uniqueBase + InstalledPostfix;
}

Expand All @@ -29,11 +29,11 @@ public static string CreateUniqueName()
/// appending a postfix, so that that the resulting path follows a different
/// pattern and can be discovered using a different file mask.
/// For example, for the _installed_ path
/// ".../20190710-1234.567890.r"
/// ".../1907101234567r"
/// the _installing_ path will be:
/// ".../20190710-1234.567890.r.i"
/// This makes it possible to enumerate all the installed snapshots by using ".../*.r" mask,
/// and all the installing snapshots by using ".../*.i" mask.
/// ".../1907101234567ri"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be 1907101234567i?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnatoliB: Please rebase your code. The unsuccessful checks should pass now :).

Copy link
Contributor Author

@AnatoliB AnatoliB Jul 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Francisco-Gamino

Should this be 1907101234567i?

It could, but the code will actually do 1907101234567ri, for the sake of simplifying the transformation from the "ready" name to the "installing" name. Currently, we just append i, which is a no-brainer. If we wanted to do 1907101234567i instead, we would have to do a replacement, make a decision on how to handle the situation when the original name does not end with "i". Though not very difficult, these decisions complicate the problem without a valuable reason.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Thanks for the clarification @AnatoliB.

/// This makes it possible to enumerate all the installed snapshots by using ".../*r" mask,
/// and all the installing snapshots by using ".../*i" mask.
/// </summary>
public static string ConvertInstalledToInstalling(string installedPath)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class DependencySnapshotFolderNameToolsTests
public void CreatesUniqueEnoughNames()
{
var name1 = DependencySnapshotFolderNameTools.CreateUniqueName();
Thread.Sleep(1); // A snapshot name created 1 millisecond later must be different
Thread.Sleep(2); // A snapshot name created 2 milliseconds later must be different
var name2 = DependencySnapshotFolderNameTools.CreateUniqueName();
Assert.NotEqual(name1, name2);
}
Expand All @@ -41,7 +41,7 @@ public void NamesConvertedFromInstalledToInstallingDoNotHaveInstalledPostfix()
public void UniqueNamesConvertedFromInstalledToInstallingAreStillUnique()
{
var name1 = DependencySnapshotFolderNameTools.CreateUniqueName();
Thread.Sleep(1); // A snapshot name created 1 millisecond later must be different
Thread.Sleep(2); // A snapshot name created 2 milliseconds later must be different
var name2 = DependencySnapshotFolderNameTools.CreateUniqueName();

var convertedToInstalling1 = DependencySnapshotFolderNameTools.ConvertInstalledToInstalling(name1);
Expand Down