Skip to content

Commit 1b0366a

Browse files
author
Jamie Hannaford
committed
Merge pull request #481 from ycombinator/del-container-recursive
Reliably delete a container with its objects
2 parents 2edaf51 + 4022318 commit 1b0366a

File tree

2 files changed

+40
-48
lines changed

2 files changed

+40
-48
lines changed

lib/OpenCloud/ObjectStore/Resource/Container.php

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,8 @@ public function getBytesQuota()
159159
public function delete($deleteObjects = false)
160160
{
161161
if ($deleteObjects === true) {
162-
if (!$this->deleteAllObjects()) {
163-
throw new ContainerException('Could not delete all objects within container. Cannot delete container.');
164-
}
162+
// Delegate to auxiliary method
163+
return $this->deleteWithObjects();
165164
}
166165

167166
try {
@@ -178,6 +177,40 @@ public function delete($deleteObjects = false)
178177
}
179178
}
180179

180+
public function deleteWithObjects($secondsToWait = null)
181+
{
182+
// If timeout (seconds to wait) is not specified by caller, try to
183+
// estimate it based on number of objects in container
184+
if (null === $secondsToWait) {
185+
$numObjects = (int) $this->retrieveMetadata()->getProperty('Object-Count');
186+
$secondsToWait = round($numObjects / 2);
187+
}
188+
189+
// Attempt to delete all objects and container
190+
$endTime = time() + $secondsToWait;
191+
$containerDeleted = false;
192+
while ((time() < $endTime) && !$containerDeleted) {
193+
$this->deleteAllObjects();
194+
try {
195+
$response = $this->delete();
196+
$containerDeleted = true;
197+
} catch (ContainerException $e) {
198+
// Ignore exception and try again
199+
} catch (ClientErrorResponseException $e) {
200+
if ($e->getResponse()->getStatusCode() == 404) {
201+
// Container has been deleted
202+
$containerDeleted = true;
203+
} else {
204+
throw $e;
205+
}
206+
}
207+
}
208+
if (!$containerDeleted) {
209+
throw new ContainerException('Container and all its objects cound not be deleted');
210+
}
211+
return $response;
212+
}
213+
181214
/**
182215
* Deletes all objects that this container currently contains. Useful when doing operations (like a delete) that
183216
* require an empty container first.
@@ -187,39 +220,11 @@ public function delete($deleteObjects = false)
187220
public function deleteAllObjects()
188221
{
189222
$paths = array();
190-
191223
$objects = $this->objectList();
192-
193224
foreach ($objects as $object) {
194225
$paths[] = sprintf('/%s/%s', $this->getName(), $object->getName());
195226
}
196-
197-
$this->getService()->batchDelete($paths);
198-
199-
return $this->waitUntilEmpty();
200-
}
201-
202-
/**
203-
* This is a method that makes batch deletions more convenient. It continually
204-
* polls the resource, waiting for its state to change. If the loop exceeds the
205-
* provided timeout, it breaks and returns FALSE.
206-
*
207-
* @param int $secondsToWait The number of seconds to run the loop
208-
* @return bool
209-
*/
210-
public function waitUntilEmpty($secondsToWait = 60, $interval = 1)
211-
{
212-
$endTime = time() + $secondsToWait;
213-
214-
while (time() < $endTime) {
215-
if ((int) $this->retrieveMetadata()->getProperty('Object-Count') === 0) {
216-
return true;
217-
}
218-
219-
sleep($interval);
220-
}
221-
222-
return false;
227+
return $this->getService()->batchDelete($paths);
223228
}
224229

225230
/**

tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ public function test_NonCDN_Container()
9797
public function test_Delete()
9898
{
9999
$container = $this->container;
100+
101+
$this->addMockSubscriber(new Response(204, array('X-Container-Object-Count' => 5)));
100102
$this->addMockSubscriber($this->makeResponse('[]', 200));
103+
101104
$response = $container->delete(true);
102105
$this->isResponse($response);
103106
}
@@ -335,20 +338,4 @@ public function test_Object_Exists_False()
335338
{
336339
$this->assertFalse($this->container->objectExists('test.foo'));
337340
}
338-
339-
public function test_Waiter_Returns_True_For_Empty_Container()
340-
{
341-
$response = new Response(204, array('X-Container-Object-Count' => 0));
342-
$this->addMockSubscriber($response);
343-
344-
$this->assertTrue($this->container->waitUntilEmpty(2, 0));
345-
}
346-
347-
public function test_Waiter_Returns_False_On_Timeout()
348-
{
349-
$response = new Response(204, array('X-Container-Object-Count' => 10));
350-
$this->addMockSubscriber($response);
351-
352-
$this->assertFalse($this->container->waitUntilEmpty(0.1, 1));
353-
}
354341
}

0 commit comments

Comments
 (0)