Skip to content

Commit

Permalink
fix: improve docs/throw more relevant error in regards to memory util…
Browse files Browse the repository at this point in the history
…ization for session pool (#5470)

* fix: improve docs/throw more relevant error in regards to memory utilization for session pool

* Update Spanner/src/Session/CacheSessionPool.php

Co-authored-by: Brent Shaffer <[email protected]>

* add throws annotation to more methods

Co-authored-by: Brent Shaffer <[email protected]>
  • Loading branch information
2 people authored and vishwarajanand committed Aug 29, 2022
1 parent 809c90b commit 3cd93a1
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 13 deletions.
7 changes: 6 additions & 1 deletion Spanner/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ use Google\Auth\Cache\SysVCacheItemPool;
$authCache = new SysVCacheItemPool();
$sessionCache = new SysVCacheItemPool([
// Use a different project identifier for ftok than the default
'proj' => 'B'
'proj' => 'B',
// We highly recommend using 250kb as it should safely contain the default
// 500 maximum sessions the pool can handle. Please modify this value
// accordingly depending on the number of maximum sessions you would like
// for the pool to handle.
'memsize' => 250000
]);

$spanner = new SpannerClient([
Expand Down
57 changes: 45 additions & 12 deletions Spanner/src/Session/CacheSessionPool.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Grpc\UnaryCall;
use GuzzleHttp\Promise;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;

/**
Expand All @@ -35,7 +36,12 @@
* We provide `Google\Auth\Cache\SysVCacheItemPool`, which is a fast PSR-6
* implementation in `google/auth` library. If your PHP has `sysvshm`
* extension enabled (most binary distributions have it compiled in), consider
* using it.
* using it. Please note the SysVCacheItemPool implementation defaults to a
* memory allotment that may not meet your requirements. We recommend setting
* the memsize setting to 250000 (250kb) as it should safely contain the default
* 500 maximum sessions the pool can handle. Please modify this value
* accordingly depending on the number of maximum sessions you would like
* for the pool to handle.
*
* Please note that when
* {@see Google\Cloud\Spanner\Session\CacheSessionPool::acquire()} is called at
Expand Down Expand Up @@ -239,7 +245,7 @@ public function acquire($context = SessionPoolInterface::CONTEXT_READ)
}

if ($shouldSave) {
$this->cacheItemPool->save($item->set($data));
$this->save($item->set($data));
}

return [$session, $toCreate];
Expand Down Expand Up @@ -277,7 +283,7 @@ public function acquire($context = SessionPoolInterface::CONTEXT_READ)
}
}

$this->cacheItemPool->save($item->set($data));
$this->save($item->set($data));

return $session;
});
Expand Down Expand Up @@ -308,6 +314,7 @@ public function acquire($context = SessionPoolInterface::CONTEXT_READ)
* Release a session back to the pool.
*
* @param Session $session The session.
* @throws \RuntimeException
*/
public function release(Session $session)
{
Expand All @@ -323,7 +330,7 @@ public function release(Session $session)
'expiration' => $session->expiration()
?: $this->time() + SessionPoolInterface::SESSION_EXPIRATION_SECONDS
]);
$this->cacheItemPool->save($item->set($data));
$this->save($item->set($data));
}
});
}
Expand All @@ -339,6 +346,7 @@ public function release(Session $session)
* to keep your session active.
*
* @param Session $session The session to keep alive.
* @throws \RuntimeException
*/
public function keepAlive(Session $session)
{
Expand All @@ -347,7 +355,7 @@ public function keepAlive(Session $session)
$data = $item->get();
$data['inUse'][$session->name()]['lastActive'] = $this->time();

$this->cacheItemPool->save($item->set($data));
$this->save($item->set($data));
});
}

Expand All @@ -365,6 +373,7 @@ public function keepAlive(Session $session)
* between 1 and 100.
* @return int The number of sessions removed from the pool.
* @throws \InvaldArgumentException
* @throws \RuntimeException
*/
public function downsize($percent)
{
Expand All @@ -384,7 +393,7 @@ public function downsize($percent)
$toDelete = array_splice($data['queue'], (int) -$countToDelete);
}

$this->cacheItemPool->save($item->set($data));
$this->save($item->set($data));
return $toDelete;
});

Expand All @@ -407,6 +416,7 @@ public function downsize($percent)
* Create enough sessions to meet the minimum session constraint.
*
* @return int The number of sessions created and added to the queue.
* @throws \RuntimeException
*/
public function warmup()
{
Expand All @@ -419,7 +429,7 @@ public function warmup()
if ($count < $this->config['minSessions']) {
$toCreate = $this->buildToCreateList($this->config['minSessions'] - $count);
$data['toCreate'] += $toCreate;
$this->cacheItemPool->save($item->set($data));
$this->save($item->set($data));
}

return $toCreate;
Expand All @@ -443,7 +453,7 @@ public function warmup()
unset($data['toCreate'][$id]);
}

$this->cacheItemPool->save($item->set($data));
$this->save($item->set($data));
});

if ($exception) {
Expand Down Expand Up @@ -710,7 +720,7 @@ private function handleSession(array $session)
$item = $this->cacheItemPool->getItem($this->cacheKey);
$data = $item->get();
unset($data['inUse'][$session['name']]);
$this->cacheItemPool->save($item->set($data));
$this->save($item->set($data));
});
}

Expand All @@ -731,12 +741,12 @@ private function waitForNextAvailableSession()
$session = $this->getSession($data);

if ($session) {
$this->cacheItemPool->save($item->set($data));
$this->save($item->set($data));
return $session;
}

if ($this->config['maxCyclesToWaitForSession'] <= $elapsedCycles) {
$this->cacheItemPool->save($item->set($data));
$this->save($item->set($data));

throw new \RuntimeException(
'A session did not become available in the allotted number of attempts.'
Expand Down Expand Up @@ -972,7 +982,7 @@ public function maintain()
$cachedData['maintainTime'] = $this->time();
// Put extra sessions to the end of the queue, so they won't be acquired until really needed.
$cachedData['queue'] = array_merge($sessions, $extraSessions);
$this->cacheItemPool->save($cacheItem->set($cachedData));
$this->save($cacheItem->set($cachedData));
});
}

Expand All @@ -989,4 +999,27 @@ private function refreshSession($session)
return false;
}
}

/**
* @param CacheItemInterface $item
* @throws \RuntimeException
*/
private function save(CacheItemInterface $item)
{
$status = $this->cacheItemPool->save($item);

if (!$status) {
throw new \RuntimeException(
'Failed to save session pool data. This can often be related to ' .
'your chosen cache implementation running out of memory. ' .
'If so, please attempt to configure a greater memory alottment ' .
'and try again. When using the Google\Auth\Cache\SysVCacheItemPool ' .
'implementation we recommend setting the memory allottment to ' .
'250000 (250kb) in order to safely handle the default maximum ' .
'of 500 sessions handled by the pool. If you require more ' .
'maximum sessions please plan accordingly and increase the memory ' .
'allocation.'
);
}
}
}
32 changes: 32 additions & 0 deletions Spanner/tests/Unit/Session/CacheSessionPoolTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use Google\Cloud\Spanner\Session\Session;
use Google\Cloud\Core\Testing\GrpcTestTrait;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Prophecy\Argument;
use Prophecy\Argument\ArgumentsWildcard;
Expand Down Expand Up @@ -85,6 +86,37 @@ public function badConfigDataProvider()
];
}

public function testAcquireThrowsExceptionUnableToSaveItem()
{
$this->expectException('\RuntimeException');
$this->expectExceptionMessage(
'Failed to save session pool data. This can often be related to ' .
'your chosen cache implementation running out of memory. ' .
'If so, please attempt to configure a greater memory alottment ' .
'and try again. When using the Google\Auth\Cache\SysVCacheItemPool ' .
'implementation we recommend setting the memory allottment to ' .
'250000 (250kb) in order to safely handle the default maximum ' .
'of 500 sessions handled by the pool. If you require more ' .
'maximum sessions please plan accordingly and increase the memory ' .
'allocation.'
);
$config = ['maxSessions' => 1];
$cacheItem = $this->prophesize(CacheItemInterface::class);
$cacheItem->get()
->willReturn(null);
$cacheItem->set(Argument::any())
->willReturn($cacheItem);
$cacheItemPool = $this->prophesize(CacheItemPoolInterface::class);
$cacheItemPool->save(Argument::any())
->willReturn(false);
$cacheItemPool->getItem(Argument::any())
->willReturn($cacheItem->reveal());

$pool = new CacheSessionPoolStub($cacheItemPool->reveal(), $config, $this->time);
$pool->setDatabase($this->getDatabase());
$pool->acquire();
}

public function testAcquireThrowsExceptionWhenMaxCyclesMet()
{
$this->expectException('\RuntimeException');
Expand Down

0 comments on commit 3cd93a1

Please sign in to comment.