Skip to content

Commit

Permalink
feat: Storm cache supports millisecond resolution (#1011)
Browse files Browse the repository at this point in the history
The Storm Cache now supports milliseconds wait times.
When the a storm cache is empty it will also
only wait for a maximum inFlightTTL.

This means that the maximum number of serial requests
that will be waited for
before an error is now graceInterval / inFlightTTL.

In this situation, the cache will return/throw InFlightTTLExceeded.

Under continuous failure situations having inFlightTTL
and graceInterval be seconds can result in a continuous buildup of load.

Consider a situation where a specific branch key
will never be successfully added to the storm cache.
Under continuous load the storm cache will turn
this load from concurrent requests to serial requests.

However, if a failure is relatively fast, like 25ms,
then this serial handling of load
at a minimum of 1 request per second
will quickly cause the system to be overwhelmed.
  • Loading branch information
seebees authored Nov 19, 2024
1 parent 46d9977 commit 6f09d5d
Show file tree
Hide file tree
Showing 23 changed files with 903 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1623,12 +1623,16 @@ module {:extern "software.amazon.cryptography.materialproviders.internaldafny.ty
nameonly graceInterval: CountingNumber ,
nameonly fanOut: CountingNumber ,
nameonly inFlightTTL: CountingNumber ,
nameonly sleepMilli: CountingNumber
nameonly sleepMilli: CountingNumber ,
nameonly timeUnits: Option<TimeUnits> := Option.None
)
datatype SymmetricSignatureAlgorithm =
| HMAC(HMAC: AwsCryptographyPrimitivesTypes.DigestAlgorithm)
| None(None: None)
type SymmetricSigningKeyList = seq<Secret>
datatype TimeUnits =
| Seconds
| Milliseconds
datatype UpdateUsageMetadataInput = | UpdateUsageMetadataInput (
nameonly identifier: seq<uint8> ,
nameonly bytesUsed: PositiveInteger
Expand Down Expand Up @@ -1661,6 +1665,9 @@ module {:extern "software.amazon.cryptography.materialproviders.internaldafny.ty
| EntryDoesNotExist (
nameonly message: string
)
| InFlightTTLExceeded (
nameonly message: string
)
| InvalidAlgorithmSuiteInfo (
nameonly message: string
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ structure EntryAlreadyExists {
message: String,
}

@error("client")
structure InFlightTTLExceeded {
@required
message: String,
}

// Materials Cache Constructors

@positional
Expand Down Expand Up @@ -190,26 +196,43 @@ structure StormTrackingCache {
entryPruningTailSize: CountingNumber,

@required
@javadoc("How many seconds before expiration should an attempt be made to refresh the materials.
@javadoc("How much time before expiration should an attempt be made to refresh the materials.
If zero, use a simple cache with no storm tracking.")
gracePeriod: CountingNumber,

@required
@javadoc("How many seconds between attempts to refresh the materials.")
@javadoc("How much time between attempts to refresh the materials.")
graceInterval: CountingNumber,

@required
@javadoc("How many simultaneous attempts to refresh the materials.")
fanOut: CountingNumber,

@required
@javadoc("How many seconds until an attempt to refresh the materials should be forgotten.")
@javadoc("How much time until an attempt to refresh the materials should be forgotten.")
inFlightTTL: CountingNumber,

@required
@javadoc("How many milliseconds should a thread sleep if fanOut is exceeded.")
sleepMilli: CountingNumber,
}

@javadoc("The time unit for gracePeriod, graceInterval, and inFlightTTL.
The default is seconds.
If this is set to milliseconds, then these values will be treated as milliseconds.")
timeUnits: TimeUnits
}

@enum([
{
name: "Seconds",
value: "Seconds",
},
{
name: "Milliseconds",
value: "Milliseconds",
},
])
string TimeUnits

union CacheType {
Default : DefaultCache,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,11 +269,15 @@ module AwsCryptographyMaterialProvidersOperations refines AbstractAwsCryptograph
method CheckCache(cache : CacheType, ttlSeconds: PositiveLong) returns (output : Outcome<Error>)
{
if cache.StormTracking? {
var gracePeriod := if cache.StormTracking.timeUnits.UnwrapOr(Types.Seconds).Seconds? then
cache.StormTracking.gracePeriod as PositiveLong
else
cache.StormTracking.gracePeriod as PositiveLong / 1000;
var storm := cache.StormTracking;
if ttlSeconds <= storm.gracePeriod as PositiveLong {
if ttlSeconds <= gracePeriod {
var msg := "When creating an AwsKmsHierarchicalKeyring with a StormCache, " +
"the ttlSeconds of the KeyRing must be greater than the gracePeriod of the StormCache " +
"yet the ttlSeconds is " + N(ttlSeconds) + " and the gracePeriod is " + N(storm.gracePeriod as PositiveLong) + ".";
"yet the ttlSeconds is " + N(ttlSeconds) + " and the gracePeriod is " + N(gracePeriod) + ".";
return Fail(Types.AwsCryptographicMaterialProvidersException(message := msg));
}
return Pass;
Expand Down Expand Up @@ -819,6 +823,8 @@ module AwsCryptographyMaterialProvidersOperations refines AbstractAwsCryptograph
case StormTracking(c) =>
var cache := c.( entryPruningTailSize := OptionalCountingNumber(c.entryPruningTailSize));
:- StormTracker.CheckSettings(cache);


var cmc := new StormTracker.StormTracker(cache);
var synCmc := new StormTrackingCMC.StormTrackingCMC(cmc);
return Success(synCmc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ module {:options "/functionSyntax:4" } LocalCMC {
ensures GetCacheEntryEnsuresPublicly(input, output)
ensures unchanged(History)
ensures InternalModifies <= old(InternalModifies)
ensures output.Success? ==> now <= output.value.expiryTime
{
if cache.HasKey(input.identifier) {
var entry := cache.Select(input.identifier);
Expand Down
Loading

0 comments on commit 6f09d5d

Please sign in to comment.