Look in your second stage recycle bin in SharePoint 2007.  If you see lots of items that are older than the configured policy (default is 30 days), then there are one of a few things happening in your environment.  Your recycle bin cleanup job might be disabled, or you might be running into a bug in SharePoint where it cannot clean the items up because there are too many of them or the items are too large.

Note that this problem does not exist for SharePoint 2010.

What is the Second Stage Recycle Bin?

The Recycle Bin was introduced in SharePoint 2007 as a way for people to delete items with the ability to undo that action at some later point.  Pretty self-explanatory, it’s just like the recycle bin in Windows.  This recycle bin is commonly referred to as the user recycle bin, because the user has the ability to delete and restore items to the recycle bin.

image

Notice the verbiage at the top of that image that says items more than 30 days old are automatically deleted.  When you delete the items from the recycle bin, they aren’t really deleted.  They go into what is called the second stage recycle bin.  This recycle bin is only accessible by site collection administrators, giving them the ability to restore items.  You can see this in the Site Collection Recycle Bin.

image

Here, we see that our item is in the End User Recycle Bin.  If we delete the item from here or if we click the “Empty Recycle Bin” button, our item is not deleted, it instead ends up in the “Deleted from end user Recycle Bin” view, which is what is also called the second-stage recycle bin. 

image

If you manually delete an item here, it is immediately deleted.  By deleted I mean that the item is deleted from the content database.  If an item is left in here for 30 days (the default), then the recycle bin cleanup job will permanently delete the items from the content database.

Why Is My Second Stage Recycle Bin Not Emptying?

Simply put, the recycle bin cleanup job in SharePoint 2007 is not efficient and can time out if there are too many items or if the items are sufficiently large.  Sorry, I don’t have hard limits to share as there are a lot of variables at play.  The symptom that you may see is that even though you have the timer job configured to clean the items up, they remain in the second stage recycle bin. 

If you want to see the problem in action, then try this PowerShell script.  I am leveraging the Get-SPWeb cmdlet from PowerShell.nu’s MOSS 2007 Script Collection.

function Add-LotsOfAnnouncements2()
{
    $OpenWeb = Get-SPWeb http://w2k3mossx64

    $Announcement = $OpenWeb.Lists["Announcements"]

    for($j=0;$j -lt 1000000000;$j++)
    {
        $t = "Item " + $j;
        $NewItem = $Announcement.Items.Add()
        $NewItem["Title"] = $t
        $NewItem["Body"] = "item"
        $NewItem["Expires"] = (Get-Date).AddDays(10)
        $NewItem.Update()            
        $toss = $NewItem.Recycle()
    }


    $OpenWeb.Dispose()

}

This adds a new item, then immediately sends it to the recycle bin.  Let it run for while, and you will see items piling up in the end user recycle bin.  At some later point, empty the end user recycle bin, moving the items to the second stage recycle bin.  After the amount of time configured in your policy expires, the recycle bin timer job will attempt to delete the items and will time out. 

The problem is discussed in the post Deleting Very large objects from the second stage recycle bin.  While we are deleting many, many objects instead of very large objects, the symptoms are the same.  Note that this problem does not exist for SharePoint 2010, this only pertains to SharePoint 2007.

Check the Recycle Bin Cleanup Job

You can check the status of the Recycle Bin cleanup job in Central Administration > Operations > Timer Job Definitions.  This is configured per web application.  You can see the list of recycle bin jobs and quickly see if they are enabled or not.

image

Click on one of the entries for Recycle Bin that corresponds to your web application, and you will see the last run date.

image

This timer job The job-recycle-bin-cleanup job is configured to run daily between 11pm and 6am.  You can check this using stsadm.exe:

stsadm -o getproperty -pn job-recycle-bin-cleanup -url http://moss

The output will be something like the following.

<Property Exist="Yes" Value="daily between 22:00:00 and 06:00:00" />

There are examples of getting and setting different values for this timer job in the online documentation.

Configure Policy for the Recycle Bin for the Web Application

The recycle bin policy is configured at the web application level.  This is important to emphasize because changes you make here affect all site collections within the web application.  You can find the configuration in Central Administration > Application Management > Web Application General Settings, scroll to the bottom of the page.

image

The default is to delete items in the recycle bin after 30 days, with 50% of the live site quota for second stage deleted items.  If you are running into the problem where items are piling up in the second stage recycle bin, you should decrease this number so that items do not live in the recycle bin for so long.  This decreases the number of items that need to be deleted at a time. 

If you cannot decrease this number, then a workaround is to clean up the items manually.  You can do this with a custom timer job, a custom executable that is scheduled to run periodically, or some other process.  Below I show a PowerShell script that will empty the items in the second stage recycle bin.

To avoid having a lot of items in the second stage recycle bin, another option is to disable the second stage recycle bin by choosing the “Off” radio button.  Be aware that this will turn the second stage recycle bin off for the entire web application.  When you turn the second stage recycle bin off, the items will be immediately purged from the database, there is no undo operation here.  Remember that this applies for all site collections in the web application, it is not a site-collection level setting, so keep that in mind if you are concerned about losing data. 

Impact of Quotas on the Recycle Bin

The second stage recycle bin is configured by default to use 50% of a site’s quota.  I applied a quota template to my site before running the above PowerShell script, and received this error once my site reached its quota.

"Your changes could not be saved because this SharePoint Web site has exceeded the storage quota limit.  You must save your work to another location.  Contact your administrator to change the quota limits for the Web site."

Once you reach quota, you can also receive errors while trying to delete items from the second stage recycle bin.  The workaround here is to either increase the quota temporarily, or to disable the site quota.  Once you’ve increased the quota or disabled it, you can then perform deletes in most cases. 

PowerShell Script to Clean Up Second Stage Recycle Bin

If you went through the steps above and confirmed you have the problem outlined above, then you can clean the recycle bin manually before addressing the problem (reducing the length of time, disabling the second stage recycle bin, deleting without sending to the recycle bin by using the server side object model).  You can clean the items manually, likely one at a time to avoid timeouts and exceptions, or you can use a script to automate the task such as the PowerShell script below.

 

param([string]$Url, [switch]$help)

[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

function GetHelp() 
{
$HelpText = @"

DESCRIPTION:
NAME: Remove-SPSiteSecondStageRecycleBin
Empties the second-stage recycle bin for a Microsoft.SharePoint.SPSite Collection

PARAMETERS: 
-url        Url to SharePoint Site Collection

SYNTAX:

Remove-SPSiteSecondStageRecycleBin -url http://moss

Empties the second stage recycle bin for the SiteCollection.

Remove-SPSiteSecondStageRecycleBin -help

Displays the help topic for the script

"@
$HelpText
}

function Remove-SPSiteSecondStageRecycleBin([string]$url) 
{
    $siteCollection = New-Object Microsoft.SharePoint.SPSite($url);  
        
    $recycleQuery = New-Object Microsoft.SharePoint.SPRecycleBinQuery;
    $recycleQuery.ItemState = [Microsoft.SharePoint.SPRecycleBinItemState]::SecondStageRecycleBin;
    $recycleQuery.OrderBy = [Microsoft.SharePoint.SPRecycleBinOrderBy]::Default;

    $recycledItems = $siteCollection.GetRecycleBinItems($recycleQuery);

    $count = $recycledItems.Count;
                        
    for($i = 0; $i -lt $count; $i++)
    {
        $g = New-Object System.Guid($recycledItems[$i].ID);
        $recycledItems.Delete($g);
    }
    
 
    $siteCollection.Dispose()
}

if($help) { GetHelp; Continue }
if($url) { Remove-SPSiteSecondStageRecycleBin -url $url }

This is pretty straightforward.  Rather than try to lock the table and delete all of the items, this code simply iterates the list one by one and deletes each item.  I tested it by deleting items and emptying the recycle bin both as the same user, then deleting items as one user and emptying the recycle bin as another, both worked as expected without incident. 

 

Resources

Powershell Tutorial - Conditional Logic Using Loops

Job-recycle-bin-cleanup: Stsadm property (Office SharePoint Server)

Deleting Very large objects from the second stage recycle bin

PowerShell.nu’s MOSS 2007 Script Collection