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
44 changes: 42 additions & 2 deletions src/DatabaseL2.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected function prefixTable($base_name)
return $this->table_prefix . $base_name;
}

protected function cleanUp()
protected function pruneReplacedEvents()
{
// No deletions, nothing to do.
if (empty($this->address_deletion_patterns)) {
Expand Down Expand Up @@ -64,7 +64,47 @@ protected function cleanUp()

public function __destruct()
{
$this->cleanUp();
$this->pruneReplacedEvents();
}

public function countGarbage()
{
try {
$sth = $this->dbh->prepare('SELECT COUNT(*) garbage FROM ' . $this->prefixTable('lcache_events') . ' WHERE "expiration" < :now');
$sth->bindValue(':now', REQUEST_TIME, \PDO::PARAM_INT);
$sth->execute();
} catch (\PDOException $e) {
$this->logSchemaIssueOrRethrow('Failed to count garbage', $e);
return null;
}

$count = $sth->fetchObject();
return intval($count->garbage);
}

public function collectGarbage($item_limit = null)
{
$sql = 'DELETE FROM ' . $this->prefixTable('lcache_events') . ' WHERE "expiration" < :now';
// This is not supported by standard SQLite.
// @codeCoverageIgnoreStart
if (!is_null($item_limit)) {
$sql .= ' ORDER BY "event_id" LIMIT :item_limit';
}
// @codeCoverageIgnoreEnd
try {
$sth = $this->dbh->prepare($sql);
$sth->bindValue(':now', REQUEST_TIME, \PDO::PARAM_INT);
// This is not supported by standard SQLite.
// @codeCoverageIgnoreStart
if (!is_null($item_limit)) {
$sth->bindValue(':item_limit', $item_limit, \PDO::PARAM_INT);
}
// @codeCoverageIgnoreEnd
$sth->execute();
} catch (\PDOException $e) {
$this->logSchemaIssueOrRethrow('Failed to collect garbage', $e);
return false;
}
}

protected function queueDeletion(Address $address)
Expand Down
5 changes: 5 additions & 0 deletions src/Integrated.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,9 @@ public function getPool()
{
return $this->l1->getPool();
}

public function collectGarbage($item_limit = null)
{
return $this->l2->collectGarbage($item_limit);
}
}
2 changes: 2 additions & 0 deletions src/L2.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ abstract public function set($pool, Address $address, $value = null, $ttl = null
abstract public function delete($pool, Address $address);
abstract public function deleteTag(L1 $l1, $tag);
abstract public function getAddressesForTag($tag);
abstract public function collectGarbage($item_limit = null);
abstract public function countGarbage();
}
25 changes: 25 additions & 0 deletions src/StaticL2.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,31 @@ public function __construct()
$this->tags = [];
}

public function countGarbage()
{
$garbage = 0;
foreach ($this->events as $event_id => $entry) {
if ($entry->expiration < REQUEST_TIME) {
$garbage++;
}
}
return $garbage;
}

public function collectGarbage($item_limit = null)
{
$deleted = 0;
foreach ($this->events as $event_id => $entry) {
if ($entry->expiration < REQUEST_TIME) {
unset($this->events[$event_id]);
$deleted++;
}
if ($deleted === $item_limit) {
break;
}
}
}

// Returns an LCache\Entry
public function getEntry(Address $address)
{
Expand Down
47 changes: 45 additions & 2 deletions tests/LCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,11 @@ public function testBrokenDatabaseFallback()

// Try applying events to an uninitialized L1.
$this->assertNull($l2->applyEvents(new StaticL1()));

// Try garbage collection routines.
$pool->collectGarbage();
$count = $l2->countGarbage();
$this->assertNull($count);
}

public function testDatabaseL2SyncWithNoWrites()
Expand Down Expand Up @@ -638,17 +643,54 @@ public function performFailedUnserializationOnSyncTest($l2)
$this->assertEquals(1, $l1->getMisses());
}

public function testDatabaseL2FailedUnserializationOnSyncTest() {
public function testDatabaseL2FailedUnserializationOnSync()
{
$this->createSchema();
$l2 = new DatabaseL2($this->dbh);
$this->performFailedUnserializationOnSyncTest($l2);
}

public function testStaticL2FailedUnserializationOnSyncTest() {
public function testStaticL2FailedUnserializationOnSync()
{
$l2 = new StaticL2();
$this->performFailedUnserializationOnSyncTest($l2);
}

public function performGarbageCollectionTest($l2)
{
$pool = new Integrated(new StaticL1(), $l2);
$myaddr = new Address('mybin', 'mykey');
$this->assertEquals(0, $l2->countGarbage());
$pool->set($myaddr, 'myvalue', -1);
$this->assertEquals(1, $l2->countGarbage());
$pool->collectGarbage();
$this->assertEquals(0, $l2->countGarbage());
}

public function testDatabaseL2GarbageCollection()
{
$this->createSchema();
$l2 = new DatabaseL2($this->dbh);
$this->performGarbageCollectionTest($l2);
}

public function testStaticL2GarbageCollection()
{
$l2 = new StaticL2();
$this->performGarbageCollectionTest($l2);

// Test item limits.
$pool = new Integrated(new StaticL1(), $l2);
$myaddr2 = new Address('mybin', 'mykey2');
$myaddr3 = new Address('mybin', 'mykey3');
$pool->collectGarbage();
$pool->set($myaddr2, 'myvalue', -1);
$pool->set($myaddr3, 'myvalue', -1);
$this->assertEquals(2, $l2->countGarbage());
$pool->collectGarbage(1);
$this->assertEquals(1, $l2->countGarbage());
}

/**
* @return PHPUnit_Extensions_Database_DataSet_IDataSet
*/
Expand All @@ -657,3 +699,4 @@ protected function getDataSet()
return new \PHPUnit_Extensions_Database_DataSet_DefaultDataSet();
}
}