| MOSS 2007 and SharePoint 2010 keep a record of the total storage consumed by each Site collection; this is presented in Central Administration within the Site Collection Quotas and Locks page and also the Storage Space Allocation page within each Site collection in MOSS 2007. This figure is used by SharePoint to determine if a Site collection is exceeding its configured storage quota.

Occasionally this figure can be incorrect and may not accurately reflect the total amount of storage that is actually being consumed by the Site collection. If you ever encounter this scenario you can tell SharePoint to recalculate this figure by using the Windows PowerShell commands below, these use the RecalculateStorageUsed method - http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spsite.recalculatestorageused.aspx. Simply replace the highlighted URL with that of the Site collection that you want to recalculate and then either run the commands manually or save as a .ps1 file and execute as a script.
MOSS 2007
[void][system.reflection.assembly]::loadwithpartialname("Microsoft.SharePoint")
$URL = "http://moss2007"
$Site = New-Object Microsoft.SharePoint.SPSite($URL)
$Site.RecalculateStorageUsed()
SharePoint 2010
$URL = "http://sp2010"
$Site = Get-SPSite -identity $URL
$Site.RecalculateStorageUsed()
|
| As described in our previous post A Closer Look At “Site use confirmation and deletion”…when deleting a site through the UI, or through PowerShell/STSADM specifying the GradualDelete option, the gradual delete approach is taken, by calling proc_DeleteSiteAsync , which marks the site as “Deleted” in the AllSites table, and an entry is written in the SiteDeletion table with the Restorable field set to 1 and InDeletion set to 0. On the next scheduled run of the Gradual Delete timer job, you would expect the gradual delete process to begin.
Running the Gradual Delete Timer Job immediately after sites that have been deleted using the above approach, has no affect against these sites – they remain unchanged in AllSites and SiteDeletion tables.
This now starts to look like all sites that are deleted through the UI or through PowerShell/STSADM specifying the GradualDelete option, end up being treated as sites in the site recycle bin, which is a little strange as when using PowerShell/STSADM to delete a site without the GradualDelete option, the proc_DeleteSite stored procedure (hard delete) is called.
However running the Recycle Bin Timer Job more than 30 days (where 30 is recycle bin default) to try and remove the sites, has no affect against these sites either, they still remain unchanged in AllSites and SiteDeletion tables.
Attempting to run the Gradual Delete Timer Job more than 30 days after the sites have been deleted removes these sites using the gradual delete approach. A SQL profiler trace shows that proc_DeleteSiteCoreAsync is called.

A look into this stored procedure shows that it deletes 1000 rows at a time. InDeletion in the SiteDeletion table will be set to 1 as soon as a delete starts but you may not always see this because it all happens fairly swiftly.
If the Site was large and not all content had been deleted yet, then the entry would still remain in the SiteDeletion table with InDeletion = 1 until all data is cleared from the tables. In this case the site was pretty small and removed in one run.
What does this mean?
What’s interesting is that the Recycle Bin Timer Job has nothing to do with site deletes i.e. has no relation to clearing sites in the SiteDeletion table that meet the criteria for being removed. It’s the Gradual Delete timer job that removes the site based on criteria defined in the Web Application Recycle Bin retention settings.
The second point to be aware of is that using PowerShell/STSADM to delete a site with the GradualDelete option will do what it says on the tin, however it won’t actually start the delete on the next run of the Gradual Delete Timer job as you may expect. In essence sites deleted through the UI and using PowerShell/STSADM to delete a site with the GradualDelete option are treated in exactly the same way.
The only way to force a gradual delete to start immediately having used PowerShell/STSADM to delete a site with the GradualDelete option, would be to use the Remove-SPDeletedSite cmdlet after having deleted the site. The cmdlet will change the value of Restorable in the SiteDeletion from 1 to 0, which will allow the Gradual Delete Timer job to begin the removal of this site the next time it runs.
That’s all for now folks, please let us know if you have any questions.
Thanks,
Sam and Chris – Microsoft UK Premier Field Engineering
|
| The aim of this post is to take a look at the Site use confirmation and deletion functionality, and more specifically the scenario of re-enabling this after having had it turned off for a period of time, and certain caveats and differences to be aware of when comparing against typical site delete approaches.
This functionality relies on the Dead Site Delete timer job. This timer job runs daily by default and after 90 days (by default) since site creation or confirmation (CertificationDate property), it will mail the owner prompting them to confirm use of the site and will increment the number of notifications sent (DeadWebNotificationCount property) by 1. If the owner “confirms use” through the mail received, this calls the ConfirmUsage() method against the site.
ConfirmUsage() updates the CertificationDate of the site to the current time and date and resets and DeadWebNotificationCount to 0. Accessing the site since creation or last confirmation has no impact on the CertificationDate property, nor does turning off and on the Site use confirmation and deletion functionality. Only a call to the ConfirmUsage() method will update this value.
What happens if we turn it back on?
Picture the scenario where it may have been turned off for several months, the reason for this may be that you were finding sites were automatically being deleted where owner details were no longer valid, so you thought you would wait until SP1 for SharePoint was released and then take advantage of the new Site Recycle Bin. When turning this back on, the first day the timer job runs, all sites with a CertificationDate older than 90 days will receive an email asking for confirmation. This will be repeated every day until the owner confirms usage, or the number of notifications (DeadWebNotificationCount) limit is reached at which point the site will be deleted. Having had it turned off for so long, the CertificationDate for the majority of site collections in the Web Application is likely to be older than 90 days, and they are likely to have mails sent out to them.
There are therefore two risks to be aware of:
- We are likely to have a large number of mails generated by SharePoint and sent out to site owners at the same point when the timer job is first run: this is something we could stagger by manually calling the ConfirmUsage() method against batches of sites before re-enabling the functionality.
- Although this could be mitigated (very slightly) by setting up some rules in Exchange, sites that have no valid owners or incorrect email addresses could automatically be deleted if site usage is not confirmed.
Point 2 is of significant concern. The reason being, when sites are deleted through this functionality, they are not stored in the site recycle bin (introduced with SP1), a hard delete occurs immediately, with no trace of the site left in SharePoint!
What’s happening under the hood?
The issue is related to the stored procedure called to carry out the site delete when the Dead Site Delete timer job runs and realises that the number of notifications to the site owner has exceeded the threshold, and it needs to take action to delete the site.
Let’s take a step back and take a look at what happens when we delete a site through the UI. If we take a site (which has ID of ccbc8865-0cbb-4bd2-94f1-96f6c2c884e0), a SQL profiler trace reveals that the stored procedure called is proc_DeleteSiteAsync.

A look into proc_DeleteSiteAsync reveals that this performs a soft delete, i.e. the site is marked as “Deleted” in the AllSites table in the content database that it resides, and an entry is written in the SiteDeletion table with the Restorable field set to 1 and InDeletion set to 0. This clearly shows that the site still exists, and is restorable if required, i.e. it is in the “Site Recycle Bin”. The site recycle bin picks up its settings from the Web Application Recycle Bin settings, so by default will retain items for 30 days.

However, when a site is deleted automatically using the Site use and confirmation and delete functionality, the behavior is different. If we take a look at a site that has “expired”, i.e. usage hasn’t been confirmed for well over 90 days and over 28 notifications have been sent (see site with ID of e99a7e97-1437-48ee-88d8-3f97bc4ecf52 in the AllSites table below), when the Dead Site Delete timer job is run, this site will be automatically deleted.

The interesting thing is that rather than using the proc_DeleteSiteAsync stored procedure, a different stored procedure, proc_DeleteSite, is called as illustrated below.

A look into proc_DeleteSite reveals that this performs a hard delete, i.e. the site is completely removed from the content database with no entry written into the SiteDeletion table.
What does this mean?
There are a couple of issues with this. Firstly, using the Site use confirmation and deletion functionality does not utilize the site Recycle Bin. This means legitimate sites could be deleted automatically if a) an invalid email address exists b) the site admin ignores the email.
The next problem using this stored procedure needs a little more background and a further look under the hood.
You may be familiar with the Gradual Site Delete capability introduced with the WSS 3.0 April 2009 CU. Before this, when deleting sites, SharePoint would delete a site and everything that belongs to it in one single transaction. This could cause severe performance issues. The issue is that when a very large site collection is being deleted in one single transaction, all the tables involved (about 82) have records that relate to this site collection removed. Every deleted record in every table in that transaction must get locked to avoid a dirty read/update on it. When the number of affected records in a single table is very high (more than a threshold having a default value of 5000), SQL Server creates a lock on the whole table rather than on each of the affected records to reduce the memory and CPU overhead of creating and releasing locks. Although this sounds more efficient, it results in making the whole table inaccessible (can’t read from or write to it) by any other operation. Knowing that many tables are affected, this can result in making sites that share the same database in SharePoint unresponsive for the whole period of deletion.
To recap, because the Dead Site Delete timer job calls proc_DeleteSite, the gradual delete simply does not happen, and can consequently lead to huge performance issues when a hard delete is automatically performed against these large site collections.
If you’re planning on using this functionality, make sure you’re aware of these caveats.. Any updates or changes to how this works will be posted here.
That’s all for now folks. Look out for another post in the next few days on how the Gradual Delete approach actually works, and some things to watch out for there too.
Thanks,
Sam and Chris – Microsoft UK Premier Field Engineering |
| Windows PowerShell is a fantastic tool; SharePoint 2010 has literally hundreds of different PowerShell Cmdlets that are available out of the box, if you don’t believe me check this out - http://technet.microsoft.com/en-us/library/ff678226.aspx. What about MOSS 2007? Whilst there aren’t any native Cmdlets for MOSS 2007, PowerShell can be used to access the SharePoint object model directly instead and in most cases achieve the same objectives, this isn’t as daunting as it sounds; I’m not a developer but even I have been able to find my way around the object model and put together some useful scripts (at least to me anyway!)
I’ve recently been helping one of my customers write some PowerShell scripts to improve their reporting capabilities and reduce the burden of day to day SharePoint administration on the support team. One of the scripts that I’ve written analyses every site collection within a Web application. The purpose of the script was to identify sites that were no longer required (as they hadn’t been updated for a long time) or that had a large quota assigned but were only using a small proportion of this, so that a smaller quota could be assigned. The script outputs the following information for each site collection into a csv file:
- URL
- Owner login
- Owner e-mail address
- Last time that the root web was modified
- The size of the quota assigned
- The total storage used
- The percentage of the quota being used
I've included the script below, all you need to do is to copy this into Notepad (or your text editor of choice), edit the two highlighted variables to match your requirements - $Output specifies the location to output the results to in csv format, $SiteURL specifies the URL of the root site collection, this will then be used to discover other site collections within the Web application. Once you have done this save the file with a .PS1 extension, for example ScriptName.PS1. The script can be run on any server in the SharePoint farm from within a Windows PowerShell window using .\ScriptName.PS1.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Sharepoint")
#Configure the location for the output file
$Output = "C:\Output.csv";
"Site URL" + "," + "Owner Login" + "," + "Owner Email" + "," + "Root Site Last Modified" + "," + "Quota Limit (MB)" + "," + "Total Storage Used (MB)" + "," + "Site Quota Percentage Used" | Out-File -Encoding Default -FilePath $Output;
#Specify the root site collection within the Web app
$Siteurl = "http://intranet.contoso.com";
$Rootweb = New-Object Microsoft.Sharepoint.Spsite($Siteurl);
$Webapp = $Rootweb.Webapplication;
#Loops through each site collection within the Web app, if the owner has an e-mail address this is written to the output file
Foreach ($Site in $Webapp.Sites)
{if ($Site.Quota.Storagemaximumlevel -gt0) {[int]$MaxStorage = $Site.Quota.StorageMaximumLevel /1MB} else {$MaxStorage = "0"};
if ($Site.Usage.Storage -gt0) {[int]$StorageUsed = $Site.Usage.Storage /1MB};
if ($Storageused -gt0 -and $Maxstorage -gt0){[int]$SiteQuotaUsed = $Storageused / $Maxstorage * 100} else {$SiteQuotaUsed = "0"};
$Web = $Site.Rootweb; $Site.Url + "," + $Site.Owner.Name + "," + $Site.Owner.Email + "," + $Web.LastItemModifiedDate.ToShortDateString() + "," + $MaxStorage + "," + $StorageUsed + "," + $SiteQuotaUsed | Out-File -Encoding Default -Append -FilePath $Output;
$Web.Dispose();$Site.Dispose()};
Below is an example of the script output.

Brendan
|
| With the release of SharePoint 2010, a feature known as backwards compatibility mode was introduced. There is very little public documentation available around this feature, so in this post I will try and clarify what it is, how it works, and a few very important things to be aware of which will impact your SharePoint update deployment plans, whether you like it or not!
What is backwards compatibility mode?
The primary reason this feature was introduced was so that if security updates affecting SharePoint are ever released on Patch Tuesday, and Microsoft Update pushes the update out to servers in a farm and to be installed., rather than forcing PSConfig to run and immediately incur downtime after the patch has been installed, the database upgrade piece can be deferred to a later date. This means you can benefit from the patch (e.g. security fix) without experiencing huge amounts of downtime. When the database upgrade is deferred, the databases are in “backwards compatibility mode”.
However, it was only ever intended that it’s used in the scenario described above, and that you run in this backwards compatibility mode for short periods of time. Running in this state for too long can cause some inconsistent behavior.
How does it work?
As mentioned above, backwards compatibility mode allows binaries on the server to be at a higher build than the databases.
But how much higher we can go? The way this is supposed to work is that the backwards compatibility mode will be re-based lined with Service Pack releases, i.e. Service Packs should introduce a compatibility boundary. A compatibly boundary would mean that after Service Pack deployment, we would not be able to upgrade binaries on the server and defer database upgrade. So we support an N-1 range of binaries on servers to database backwards compatibility where N equals a Service Pack build.
Does Service Pack 1 introduce a compatibility boundary?
Based on the description above, we’d expect Service Pack 1 (SP1) for SharePoint to introduce a compatibility boundary for SharePoint. SP1 introduces a partial boundary for compatibility mode. It is essentially the Search service which introduces a boundary, preventing indexing from functioning until after upgrade has completed across the entire farm. In theory everything else should still work except that indexing will stop and start getting stale until you upgrade, but search queries will still function across the existing index.
On top of that, there are more adverse effects of SharePoint Foundation Search, in that the service is unlikely to start, and consequently not function whatsoever. Upgrading the entire farm will ensure all of the above works as expected.
It’s also worth pointing out that only when the content databases are upgraded that important new capabilities like the site and site collection restore features will light up, which depend on database schema changes to function.
If I haven’t given you enough reasons to avoid running databases in compatibility mode post SP1 deployment, there is one scenario where you may consider to this, but proceed with caution!
In the scenario that you have Search running in a Parent or “Services” farm, it would be possible to use backwards compatibility mode to spread the upgrade out across a couple of weeks if really required. The farm not running search could have databases upgraded at a later date, but as for the services farm, the full upgrade should be carried out at once. I would only recommend running in compatibility mode as described here if you really think there’s no way you can upgrade all content within one service window, and would only suggest delaying upgrade by a couple weeks at most after SP1.
Another reason to stay away from this...
By now you’ll have gathered that staying in backwards compatibilitymode for any long periods of time is NOT a good idea.
SP1 is a good example of why this is true and future service pack releases will be even better ones. If you patch to SP1 but don't upgrade, several things will be broken or not be available, as described above.
As per the N-1 range of binaries to database backwards compatibility, thinking about a future service pack release, this could cause the entire farm to stop rendering entirely if the databases had not been upgraded to at least SP1. Once again running a farm upgrade would fix that after applying the next service pack, but one should have run upgrade long before that to prevent the unexpected outage.
Final thoughts…
It should be clear by now that this feature was designed to prevent unexpected outages due to a patch deployment, but was not meant to support an upgrade approach. You should only ever run in a state where you are patched but not upgraded for hours or days, a couple weeks at most, but not for months or longer as the above or potentially other issues could arise. A farm should be alerting you via its health rules that you when and if you are ever in this state, and if you find out that you are, you should address it ASAP.
|
| A couple of weeks ago at the SharePoint conference Chris and I delivered 3 sessions, one of which was “Deploying SharePoint 2010 as a Mission Critical Application”, in which we covered high availability options for SharePoint 2010. One of the key demonstrations showed the end-to-end configuration of high availability within a farm using SQL Server mirroring for the database tier. As promised, the PowerShell and TSQL we used is provided here in this blog post.
Firstly, credit to Mike Watson, for providing the TSQL that we’ve leveraged here: http://blogs.msdn.com/b/mikewat/archive/2008/07/30/sql-mirroring-setup-made-easy.aspx
The PowerShell demonstrated to set up a failover SQL instance for databases in a SharePoint farm is detailed below. To return all databases with no failover instance configured (except for the Logging database):
Get-SPDatabase | ? {$_.Type -ne "Microsoft.SharePoint.Administration.SPUsageDatabase"} | ? {!$_.FailoverServiceInstance}
To configure a failover instance for all databases (except for the Logging database):
Get-SPDatabase | ? {$_.Type -ne "Microsoft.SharePoint.Administration.SPUsageDatabase"} | ? {!$_.FailoverServiceInstance} | ForEach ($_) {$_.AddFailoverServiceInstance("SERVERNAME\INSTANCENAME"); $_.Update()}
Moving over to SQL, to end-to-end mirroring configuration of databases from one SQL Server instance to another is as follows.
Run the following command on the Principal, Mirror and Witness servers, to create the mirroring endpoints:
CREATE ENDPOINT Endpoint_Mirroring -- name
STATE=STARTED -- Endpoint will be started and ready
AS TCP(LISTENER_PORT=5022 -- Endpoint will use port 5022
, LISTENER_IP=ALL) -- Endpoint will listen on all IP addresses
FOR DATABASE_MIRRORING -- Specifies mirroring as the endpoint purpose
(AUTHENTICATION = WINDOWS[Negotiate] -- Will negotiate NTLM or Kerberos for authentication
, ENCRYPTION = SUPPORTED, -- Mirroring traffic will be encrypted
ROLE=ALL); -- This endpoint can be principal, mirror, or a witness
The next step is to set up full recovery model for all databases to be mirrored. Run the following on the Principal server to generate the script to achieve this:
Select 'USE MASTER; ALTER DATABASE [' + name + '] SET RECOVERY FULL;' from sys.databases where database_id > 4
The next step is to Backup all databases and logs on the Principal server. Select the output of this query, paste, and run in the query window to execute. This will backup all databases on the server except the system databases (master, temp, msdb, and model).
SELECT 'BACKUP DATABASE [' + name + '] TO DISK = ''C:\Backup\' + name + '.bak'' WITH FORMAT;' from sys.databases where database_id > 4
SELECT 'BACKUP LOG [' + name + '] TO DISK = ''C:\Backup\log_' + name + '.bak'' WITH FORMAT;' from sys.databases where database_id > 4
Now we need to generate the Restore scripts by running the following on the Principal server. The output can then be run on the Mirror server. Remember to make sure to copy the backups to the mirror server before attempting to restore! We will be restoring databases with logs with NORECOVERY.
SELECT 'RESTORE DATABASE [' + name + '] FROM DISK = ''c:\Backup\' + name + '.bak'' WITH MOVE ''' + name + ''' TO ''C:\Program Files\Microsoft SQL Server\MSSQL10_50.MIRRORING\MSSQL\DATA\' + name + '.mdf'', MOVE ''' + name + '_Log'' TO ''C:\Program Files\Microsoft SQL Server\MSSQL10_50.MIRRORING\MSSQL\DATA\' + name + '_Log.LDF'', NORECOVERY;' from sys.databases where database_id > 4
SELECT 'RESTORE LOG [' + name + '] FROM DISK = ''c:\Backup\log_' + name + '.bak'' WITH NORECOVERY;' from sys.databases where database_id > 4
At this point, something we didn’t demo during our SPC session, but may be required, is to copy SQL Server logins from the Principal server to the Mirror server as described here: http://support.microsoft.com/kb/918992
The next stage is to set up the mirroring partnerships for each database. On the Mirror server, select the output of this query, paste, and run in a query window to execute (change principal server name):
SELECT 'ALTER DATABASE [' + name + '] SET PARTNER = ''TCP://PRINCIPAL.domain.com:5022'';' from sys.databases where database_id > 4
Next we grant permissions on the endpoint on the Mirror (change “DOMAIN\user” to Principal SQL Server Service account):
use [master]
GO
GRANT CONNECT ON ENDPOINT::[Endpoint_Mirroring] TO [DOMAIN\user]
GO
Now we set up the partnership on the Principal Server, select the output of this query, paste, and run in a query window to execute (change mirror and witness server names):
SELECT 'ALTER DATABASE [' + name + '] SET PARTNER = ''TCP://MIRROR.contoso.local:5022'';' from sys.databases where database_id > 4
SELECT 'ALTER DATABASE [' + name + '] SET WITNESS = ''TCP://WITNESS.contoso.local:5022'';' from sys.databases where database_id > 4
And there we have it, the end-to-end configuration of mirroring within a SharePoint farm!
Please let us know if you have any comments, questions or feedback!
|
| One of my Customers is using DPM to backup multiple SharePoint farms. One of the Farms is still a MOSS 2007 farm. Since the SLA for the farm only requires a backup to be available for a month, they choose only to use a disk based backup, and skip the long term retention of DPM (Moving the data from disk to tape). They used the DPM storage size Calculator in order to determine the amount of disk space they needed.
The customer monitored the Database in order to determine the Change Rate in SharePoint. They have a lot of content databases and they noticed that they grew on average 1 % per day. So they used this number and inserted it into the DPM Storage calculator (Available here). The total size of the Farm in question is about 4 Terabytes.
DPM works on a block level of the file system. It utilizes the VSS (Volume Shadow Copy Service) and keeps track of all changes on the file system. The DPM Server downloads an initial snapshot of all databases, and after that he will keep doing an incremental (continuous) backup. This means that every changed block on the file system is transferred to the DPM Server. This is actually is pretty smart way of handling it, and the total amount of space needed for the backups is greatly reduced. SQL Server for example will always backup all changes since the last full backup when you do an incremental backup (Meaning each SQL differential in sequence will grow in size, since it will also contain the changes of the last SQL differential backup (or all changes since the last full backup))
Since they were monitoring the DPM Server, they noticed that the values they used differ from the actual values. With the current rate of change they would not be able to meet their SLA. They opened a case with Microsoft in order to determine why their calculations were of by so much.
Only accounting for the growth is not considering the whole picture. You also need to backup any block on the filesystem that has changed.
So what are possible changes?
- Additions
- Modifications
- Deletions
- Maintenance
The first 3 are the most obvious ones. But there is more that can cause a DB to change the layout on the drives. One thing that bit my customer hard was the maintenance jobs of SharePoint. In MOSS2007 those jobs where quite aggressive, and tried to keep the DBs “far too optimized”. This caused a lot of change on the disk level (daily index rebuilds), and increased our change rate to a huge amount (The Jobs are way better in SP2010, and this is not an issue anymore).
But my colleague Patrick Heyde already had a solution for my Problem. Disable the Timer job that does the DB-defrag, and replace it with a “smarter” implementation (only defragment when a certain threshold is reached).
Once we implemented the changes from Patrick’s Blog, we saw an immediate decrease in DPM storage. The required space for our daily snapshots of the MOSS2007 farm went down:
This graph shows the daily growth of the disk space needed by the DPM for the daily backups. At day 5 we disabled the SharePoint index rebuild job. At the end of the graph we see some negative values, since the additional space is released once the old backups phase out.
Before we disabled the maintenance Job, the daily change was approx. 270 GB/Day (9% of the Farm size). The next 13 days only have an average growth of about 56 GB/Day (1.9% of the Farm size). That’s only 21% of the original space that was required before the change. Now we are able to meet our SLAs, and even have some spare room for “huge changes” – like installing a Service pack ;)
Disclaimer: Your rates may vary depending on farm usage and various other unpredictable metrics.
|
| Update: This is now fixed in the Dec 2011 CU: http://support.microsoft.com/kb/2596998 - "When you delete a subsite of a site collection and then run the stsadm –o databaserepair command or SharePoint Health Analyzer, a report shows that the subsite was deleted incorrectly as an orphan site."
Recently I was troubleshooting an issue with orphans for a customer and whilst attempting to repro the issue in my VM, I noticed a number of SPWeb orphans such as these when running the stsadm –o databaserepair operation.

What seemed odd was that the orphans were only SPWeb orphans, and they were on my test rig which was fairly new and had only been used to test a few new features such as those in Service Pack 1. I then noticed that all the SPWeb objects listed were ones that had been deleted during my testing of the new Site Recycle Bin feature.
What does this mean?
Well, simply put, if you chose to clean-up the orphans either through stsadm or the built-in health rule (which runs monthly by default), you would actually remove any sites ‘protected’ by the Site Recycle Bin. So for example, if a user deleted a site collection yesterday, and tomorrow the health rule were to run, you would see a warning and most likely be tempted to click the repair option. Note: No orphans are actually reported in the health rule output, just the fact that you have orphans in a content database.

Choosing to repair the issue will actually remove the site collection and all child objects from the content database completely. Worse still, performing the repair could be a very resource intensive operation should the ‘orphaned’ site collection you are removing be quite large. Indeed, this was the reason the Gradual Delete Timer job was added (it deletes 1,000 objects at a time in an iterative fashion during the timer job execution window). This means that you could in fact cause database blocking and affect all site collections hosted in the same content database if you inadvertently cleaned up what you thought were a few harmless orphans.
Obviously we should all think twice before simply removing orphans, we need to think about any child object deletes that would take place. Unfortunately the health rule for this doesn’t make that easy. Therefore I’d strongly recommend always using either stsadm –o databaserepair on each content database to report on the orphans first (add –deletecorruption to repair). Alternatively use the following PowerShell to loop through all content databases:
foreach ($db in Get-SPContentDatabase) {$db.Name, $db.Repair($false)}
Is this a bug?
Well yes, but it’s a reporting bug rather than a bug causing orphans. In a nutshell, the repair command is looking at deleted Sites and Webs incorrectly and reporting them as a normal type of orphan.
Until this is fixed in an update in the hopefully near future, I’d strongly recommend that you avoid using the health analyzer’s repair automatically feature to repair any orphans that are reported (or not as the case is J), and you revert back to good old stsadm or PowerShell for more verbose information.
Should you find a number of orphaned SPWeb objects, the only way to verify that they are in fact deleted SPWeb objects in the Site Recycle Bin(which they more than likely will be), is to peek inside the associated content database. I won’t include the steps for that here, but it’s as easy as checking the AllWebs table against the SiteDeletion table. Remember that it is unsupported to perform most operations directly against a SharePoint database.
How do I remove regular orphans until the fix?
If you really need to remove any real orphans before a fix is available, then I’d suggest that you take a full backup of the offending content database and then use the Remove-SPDeletedSite cmdlet to remove all deleted site collections from the content database.
This will mark the site collection for deletion by the Gradual Delete Timer job, and is a little safer than just using the repair command to remove the site along with any other orphans listed.
Note: If you do decide to just run the repair, you will see an orphaned SPSite object after you first run the command since this will ironically create a new SPSite orphan that you need to clear by running the command again.
That’s all for now. Hopefully that saves at least one person from removing gigabytes of deleted sites during production hours! I’ll update this post when a fix comes along. |
| For those of you who are interested in SharePoint from a development perspective I'd like to point you in the direction of the Blog of my colleague Dave http://blogs.msdn.com/b/david/
Brendan Griffin |
| I was helping a customer recently to investigate a performance issue that they were experiencing in their MOSS 2007 farm which was running on Windows Server 2003 and using NTLM for authentication. During peak times throughout the day SharePoint sites were becoming incredibly slow to load, the strange thing about this issue is that it didn’t affect all WFE’s at any one time – for example we could submit the same request to an alternatives WFE and sites were loading instantly. This issue didn’t always affect the same WFE so we couldn’t isolate it to a specific server.
Here is an example of what the IIS logs looked like (trimmed for ease of reading) as you can see the user Contoso\User1 made five requests for default.aspx in each case IIS responded with a 401.2, 401.1 before the page was finally served to the end user, this is signified by a 200 response. This is a big overhead, especially when you consider all of the files that are requested during the loading of a standard out of the box SharePoint page, for example images, JavaScript and CSS files. This has the potential to put huge stress on the domain controllers and could potentially be a contributing factor to the issue.
I thought we were onto a winner; unfortunately this property was not set and IIS was therefore exhibiting the default behaviour of persisting authentication for a user when using the same connection.
UrlScan version 2.5 is a security tool that restricts the types of HTTP requests that Internet Information Services (IIS) will process. By blocking specific HTTP requests, the UrlScan security tool helps prevent potentially harmful requests from reaching the server. UrlScan 2.5 will install on servers running IIS 4.0 and later.
As a test we disabled URLScan on one of the WFE’s and this returned IIS to its default behaviour of not re-authenticating each and every request – great! But what was the root cause? It turned out to be the URLScan option to remove the server header from responses; instead of disabling URLScan (it had been installed for a reason) we simply changed the configuration via the URLScan.ini to not remove the server header from HTTP responses.

Since this has been changed no further instances of the issue have occurred. My feeling is that this issue was ultimately caused by an authentication bottleneck on the WFE, basically it couldn’t keep up with end user requests as it was unable to authenticate users quickly enough, the root cause of this issue could be that the DCs that the WFE was communicating with weren’t servicing authentication requests quickly enough and a backlog of requests was being created which would explain why Web Service\Current Connections was so high.
Chris Gideon has a fantastic post on the NTLM authentication process that explains by default a maximum of two concurrent NTLM authentication requests can be made - http://sharepoint.microsoft.com/blogs/cgideon/Lists/Posts/Post.aspx?ID=2 , this can be amended by changing the MaxConcurrentAPI setting which is one other potential resolution to NTLM authentication bottlenecks.
Brendan Griffin |
View in Web Browser /Blogs/fromthefield/_layouts/VisioWebAccess/VisioWebAccess.aspx?listguid={ListId}&itemid={ItemId}&DefaultItemOpen=1 0x0 0x1 FileType vdw 255 Compliance Details javascript:commonShowModalDialog('{SiteUrl}/_layouts/itemexpiration.aspx?ID={ItemId}&List={ListId}', 'center:1;dialogHeight:500px;dialogWidth:500px;resizable:yes;status:no;location:no;menubar:no;help:no', function GotoPageAfterClose(pageid){if(pageid == 'hold') {STSNavigate(unescape(decodeURI('{SiteUrl}'))+'/_layouts/hold.aspx?ID={ItemId}&List={ListId}'); return false;} if(pageid == 'audit') {STSNavigate(unescape(decodeURI('{SiteUrl}'))+'/_layouts/Reporting.aspx?Category=Auditing&backtype=item&ID={ItemId}&List={ListId}'); return false;} if(pageid == 'config') {STSNavigate(unescape(decodeURI('{SiteUrl}'))+'/_layouts/expirationconfig.aspx?ID={ItemId}&List={ListId}'); return false;}}, null); return false; 0x0 0x1 ContentType 0x01 898 Edit in Browser /_layouts/images/icxddoc.gif /Blogs/fromthefield/_layouts/formserver.aspx?XsnLocation={ItemUrl}&OpenIn=Browser&Source={Source} 0x0 0x1 FileType xsn 255 Edit in Browser /_layouts/images/icxddoc.gif /Blogs/fromthefield/_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser&Source={Source} 0x0 0x1 ProgId InfoPath.Document 255 Edit in Browser /_layouts/images/icxddoc.gif /Blogs/fromthefield/_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser&Source={Source} 0x0 0x1 ProgId InfoPath.Document.2 255 Edit in Browser /_layouts/images/icxddoc.gif /Blogs/fromthefield/_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser&Source={Source} 0x0 0x1 ProgId InfoPath.Document.3 255 Edit in Browser /_layouts/images/icxddoc.gif /Blogs/fromthefield/_layouts/formserver.aspx?XmlLocation={ItemUrl}&OpenIn=Browser&Source={Source} 0x0 0x1 ProgId InfoPath.Document.4 255 View in Browser /Blogs/fromthefield/_layouts/xlviewer.aspx?id={ItemUrl}&DefaultItemOpen=1 0x0 0x1 FileType xlsx 255 View in Browser /Blogs/fromthefield/_layouts/xlviewer.aspx?id={ItemUrl}&DefaultItemOpen=1 0x0 0x1 FileType xlsm 255 View in Browser /Blogs/fromthefield/_layouts/xlviewer.aspx?id={ItemUrl}&DefaultItemOpen=1 0x0 0x1 FileType xlsb 255 View in Browser /Blogs/fromthefield/_layouts/xlviewer.aspx?id={ItemUrl}&DefaultItemOpen=1 0x0 0x1 FileType ods 255 |
|
|
|
|