Skip to content

Commit

Permalink
[1.x] Implements serverless ElastiCache scaling (#242)
Browse files Browse the repository at this point in the history
* create serverless caches

* update list caches for serverless

* update allowed values

* update show command for serverless

* allow serverless scaling

* formatting

* formatting
  • Loading branch information
joedixon authored Dec 21, 2023
1 parent 7cdf6de commit 69f1338
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 23 deletions.
21 changes: 16 additions & 5 deletions src/Commands/CacheCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ public function handle()
}

$instanceClass = $this->determineInstanceClass();
$type = $this->determineCacheType();

if ($instanceClass == 'serverless') {
$instanceClass = null;
$type = 'redis7.x-serverless';
} else {
$type = $this->determineCacheType();
}

$response = $this->vapor->createCache(
$networkId,
Expand All @@ -67,7 +73,7 @@ protected function determineCacheType()
{
return $this->menu('Which type of cache would you like to create?', [
'redis6.x-cluster' => 'Redis 6.x Cluster',
'redis-cluster' => 'Redis 5.x Cluster',
'redis-cluster' => 'Redis 5.x Cluster',
]);
}

Expand All @@ -79,15 +85,20 @@ protected function determineCacheType()
protected function determineInstanceClass()
{
$type = $this->menu('Which type of cache instance would you like to create?', [
'serverless' => 'Serverless',
'general' => 'General Purpose',
'memory' => 'Memory Optimized',
'memory' => 'Memory Optimized',
]);

if ($type == 'serverless') {
return 'serverless';
}

if ($type == 'general') {
return $this->determineGeneralInstanceClass();
} else {
return $this->determineMemoryOptimizedInstanceClass();
}

return $this->determineMemoryOptimizedInstanceClass();
}

/**
Expand Down
10 changes: 9 additions & 1 deletion src/Commands/CacheListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ public function handle()
*/
protected function cacheType($type)
{
return $type == 'redis6.x-cluster' ? 'Redis 6.x Cluster' : 'Redis 5.x Cluster';
if ($type == 'redis7.x-serverless') {
return 'Redis 7.x Serverless';
}

if ($type == 'redis6.x-cluster') {
return 'Redis 6.x Cluster';
}

return 'Redis 5.x Cluster';
}
}
21 changes: 20 additions & 1 deletion src/Commands/CacheMetricsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected function configure()
$this
->setName('cache:metrics')
->addArgument('cache', InputArgument::REQUIRED, 'The cache name / ID')
->addArgument('period', InputArgument::OPTIONAL, 'The metric period (1m, 5m, 1h, 8h, 1d, 3d, 7d, 1M)', '1d')
->addArgument('period', InputArgument::OPTIONAL, 'The metric period (5m, 30m, 1h, 8h, 1d, 3d, 7d, 1M)', '1d')
->setDescription('Get usage and performance metrics for a cache');
}

Expand All @@ -43,6 +43,10 @@ public function handle()
$this->argument('period')
);

if (isset($metrics['averageCacheProcessingUnits'])) {
return $this->serverlessMetrics($metrics);
}

$this->table([
'Node', 'Average CPU Utilization', 'Cache Hits', 'Cache Misses',
], collect(range(0, count($metrics['totalCacheHits']) - 1))->map(function ($node) use ($metrics) {
Expand All @@ -54,4 +58,19 @@ public function handle()
];
})->all());
}

/**
* Format the serverless metrics for display.
*/
protected function serverlessMetrics(array $metrics): void
{
$this->table([
'Average CPU (ECPU Units)', 'Average Memory Utilization (Bytes)', 'Cache Hits', 'Cache Misses',
], [[
number_format($metrics['averageCacheProcessingUnits'][0]),
number_format($metrics['averageCacheBytesUsed'][0]),
$metrics['totalCacheHits'][0],
$metrics['totalCacheMisses'][0],
]]);
}
}
43 changes: 38 additions & 5 deletions src/Commands/CacheScaleCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Laravel\VaporCli\Helpers;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

class CacheScaleCommand extends Command
{
Expand All @@ -17,7 +18,9 @@ protected function configure()
$this
->setName('cache:scale')
->addArgument('cache', InputArgument::REQUIRED, 'The cache name / ID')
->addArgument('scale', InputArgument::REQUIRED, 'The number of nodes that should be in the cache cluster')
->addArgument('scale', InputArgument::OPTIONAL, 'The number of nodes that should be in the cache cluster')
->addOption('memory', null, InputOption::VALUE_OPTIONAL, 'The maximum amount of memory that can be used by the serverless cache')
->addOption('cpu', null, InputOption::VALUE_OPTIONAL, 'The maximum amount of ECPUs that can be used by the serverless cache')
->setDescription('Modify the number of nodes in a cache cluster');
}

Expand All @@ -40,13 +43,43 @@ public function handle()

$cache = $this->vapor->cache($cacheId);

$this->vapor->scaleCache(
$cacheId,
$this->argument('scale')
);
if ($cache['type'] === 'redis7.x-serverless') {
$this->scaleServerlessCache($cacheId);
} else {
$this->scaleCacheCluster($cacheId);
}

Helpers::info('Cache modification initiated successfully.');
Helpers::line();
Helpers::line('Caches may take several minutes to finish scaling.');
}

/**
* Scale a serverless cache.
*/
protected function scaleServerlessCache(int $cacheId): void
{
if (is_null($this->option('memory')) || is_null($this->option('cpu'))) {
Helpers::abort('You must specify both the memory and CPU limits. To remove the either limit, set it to 0.');
}

$this->vapor->scaleCache(
$cacheId,
null,
$this->option('memory') === '0' ? null : $this->option('memory'),
$this->option('cpu') === '0' ? null : $this->option('cpu')
);
}

/**
* Scale a cache cluster.
*/
protected function scaleCacheCluster(int $cacheId): void
{
if (! $scale = $this->argument('scale')) {
Helpers::abort('You must specify the number of nodes to scale the cache to.');
}

$this->vapor->scaleCache($cacheId, $scale);
}
}
54 changes: 44 additions & 10 deletions src/Commands/CacheShowCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,50 @@ public function handle()

$cache = $this->vapor->cache($cacheId);

if ($cache['type'] === 'redis7.x-serverless') {
$this->showServerlessCache($cache);
} else {
$this->showCacheCluster($cache);
}

if ($cache['endpoint']) {
Helpers::line();

Helpers::line(' <info>Endpoint:</info> '.$cache['endpoint']);
}

Helpers::line();

$this->call('cache:metrics', ['cache' => $this->argument('cache')]);
}

/**
* Render serverless cache details.
*/
protected function showServerlessCache(array $cache): void
{
$this->table([
'ID', 'Provider', 'Name', 'Region', 'Class', 'Snapshot Retention (Days)', 'Memory Limit (Bytes)', 'ECPU Limit', 'Status',
], collect([$cache])->map(function ($cache) {
return [
$cache['id'],
$cache['cloud_provider']['name'],
$cache['name'],
$cache['region'],
'Serverless',
($snapshotLimit = $cache['snapshot_retention_limit'] ?? null) ? $snapshotLimit : 'N/A',
($memoryLimit = $cache['memory_limit'] ?? null) ? $memoryLimit : 'Unlimited',
($cpuLimit = $cache['cpu_limit'] ?? null) ? $cpuLimit : 'Unlimited',
Str::title(str_replace('_', ' ', $cache['status'])),
];
})->all());
}

/**
* Render cluster cache details.
*/
protected function showCacheCluster(array $cache): void
{
$this->table([
'ID', 'Provider', 'Name', 'Region', 'Class', 'Scale', 'Status',
], collect([$cache])->map(function ($cache) {
Expand All @@ -53,15 +97,5 @@ public function handle()
Str::title(str_replace('_', ' ', $cache['status'])),
];
})->all());

if ($cache['endpoint']) {
Helpers::line();

Helpers::line(' <info>Endpoint:</info> '.$cache['endpoint']);
}

Helpers::line();

$this->call('cache:metrics', ['cache' => $this->argument('cache')]);
}
}
4 changes: 3 additions & 1 deletion src/ConsoleVaporClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -598,10 +598,12 @@ public function createCache($networkId, $name, $type, $instanceClass)
* @param int $scale
* @return void
*/
public function scaleCache($cacheId, $scale)
public function scaleCache($cacheId, $scale = null, $memoryLimit = null, $cpuLimit = null)
{
$this->requestWithErrorHandling('put', '/api/caches/'.$cacheId.'/size', [
'scale' => $scale,
'memory_limit' => $memoryLimit,
'cpu_limit' => $cpuLimit,
]);
}

Expand Down

0 comments on commit 69f1338

Please sign in to comment.