{"id":250,"date":"2009-06-09T23:45:52","date_gmt":"2009-06-09T21:45:52","guid":{"rendered":"https:\/\/www.sqlinthewild.co.za\/?p=250"},"modified":"2009-06-09T23:45:52","modified_gmt":"2009-06-09T21:45:52","slug":"deleting-the-transaction-log","status":"publish","type":"post","link":"https:\/\/www.sqlinthewild.co.za\/index.php\/2009\/06\/09\/deleting-the-transaction-log\/","title":{"rendered":"Deleting the transaction log"},"content":{"rendered":"<p>Or &#8220;<em>Why is my database now inaccessible?<\/em>&#8221;<\/p>\n<p>This is another things that I&#8217;ve seen a few things recently on various forums. A DB&#8217;s transaction log fills the disk, probably due to a lack of log backups, and then either SQL is stopped and the log file deleted or the database is detached and the log file deleted. Either way it&#8217;s not a particularly good thing to do.<\/p>\n<p>No, I&#8217;ll be blunt, it&#8217;s a appallingly bad thing to do. The transaction log is not an optional piece of the database. It&#8217;s not like an installation log or activity log where the entries are there just in case anyone&#8217;s interested. The transaction log is what SQL uses to ensure, at all times, that the data in the database is transactionally consistent. It&#8217;s what gives SQL databases the consistency and durability properties that are required from a relational database engine.<\/p>\n<p>The transaction log has a number of uses within SQL.<\/p>\n<ol>\n<li>In SQL 2000 and before, the inserted and deleted tables were materialised from the transaction log. This was changed in SQL 2005 and they&#8217;re now materialised from the row version store in TempDB.<\/li>\n<li>A transaction rollback uses the transaction log to determine what needs to be undone.<\/li>\n<li>In transactional replication, the log reader uses the transaction log to determine what changes need to be replicated<\/li>\n<li>Used by Change Data Capture in SQL 2008 to extract changes made to registered tables<\/li>\n<li>During restart-recovery to ensure that transactions that had committed when the service stopped but whose changes had not been written to the data file are replayed and to ensure that transactions that hadn&#8217;t completed are rolled back.<\/li>\n<\/ol>\n<p>For now, I&#8217;m just going to consider the last one.<\/p>\n<p><!--more-->When the SQL Service is stopped or a database detached, SQL will try to shut the database down cleanly. That means running a checkpoint and writing all dirty data pages to disk. The checkpoint has to write into the log that it has run. If there&#8217;s no more space in the log, then the checkpoint cannot run and the database cannot be cleanly shut down. This is not usually a problem. When the database is attached or the service restarted SQL will run restart-recovery on it, the transaction log will be used to bring the database back to a transactionally consistent state. That&#8217;s great, but what happens if the transaction log&#8217;s not there?<\/p>\n<p>To see exactly what will happen, I&#8217;m going to set up a database that&#8217;s got a very small max size for the log file (because I don&#8217;t want to have to fill the drive to get results), set it to full recovery and take a backup. Taking a backup will start the log chain and ensure that the log will grow until it runs out of space. Reuse of the log will not happen as I&#8217;m not taking any log backups. Once the database is backed up, I&#8217;m going to run inserts until the log fills up.<\/p>\n<p>[source:sql]CREATE DATABASE TestingLogDeletion<br \/>\nON (NAME = Test_dat,<br \/>\nFILENAME = &#8216;D:\\Develop\\Databases\\TestingLogDeletion.mdf&#8217;,<br \/>\nSIZE = 10,<br \/>\nMAXSIZE = UNLIMITED,<br \/>\nFILEGROWTH = 5 )<br \/>\nLOG ON ( NAME = Test_log,<br \/>\nFILENAME = &#8216;D:\\Develop\\Databases\\TestingLogDeletion.ldf&#8217;,<br \/>\nSIZE = 5MB,<br \/>\nMAXSIZE = 10MB);<br \/>\nGO<\/p>\n<p>ALTER DATABASE TestingLogDeletion SET RECOVERY FULL;<br \/>\nGO<\/p>\n<p>USE TestingLogDeletion<br \/>\nGO<\/p>\n<p>CREATE TABLE Filler (<br \/>\nID INT IDENTITY PRIMARY KEY,<br \/>\nFillerStr CHAR(300)<br \/>\n)<br \/>\nGO<\/p>\n<p>BACKUP DATABASE TestingLogDeletion TO DISK = &#8216;D:\\Develop\\Databases\\Backups\\TestingLogDeletion.bak'[\/source]<\/p>\n<p>Right, so that&#8217;s the setup all done. Now to fill the log up.<\/p>\n<p>[source:sql]DECLARE @i INT;<br \/>\nSET @i = 0;<\/p>\n<p>WHILE (@i &lt; 10000)<br \/>\nBEGIN<br \/>\nINSERT INTO Filler (FillerStr) VALUES (CAST(@i AS VARCHAR(10)))<br \/>\nSET @i = @i+1;<br \/>\nEND<br \/>\nGO[\/source]<\/p>\n<p>Doesn&#8217;t take long either. After just 4778 inserts, this popped up.<\/p>\n<p><span style=\"color: #ff0000;\">Msg 9002, Level 17, State 2, Line 6<br \/>\nThe transaction log for database &#8216;TestingLogDeletion&#8217; is full. To find out why space in the log cannot be reused, see the log_reuse_wait_desc column in sys.databases<\/span><\/p>\n<p>Trying to run a checkpoint gives the same error. So there&#8217;s not enough space in the log to do a clean shutdown. First I&#8217;m going to try shutting SQL down, deleting the log and then restarting SQL. See how the database handles that. Note, the highest identity value in that table was 4778 before the shutdown.<\/p>\n<p>Once SQL restarts, I&#8217;m going to query that table, see what&#8217;s in there.<\/p>\n<p>[source:sql]select MAX(ID) from TestingLogDeletion.dbo.Filler[\/source]<\/p>\n<p><span style=\"color: #ff0000;\">Msg 945, Level 14, State 2, Line 1<br \/>\nDatabase &#8216;TestingLogDeletion&#8217; cannot be opened due to inaccessible files or insufficient memory or disk space.\u00a0 See the SQL Server errorlog for details.<\/span><\/p>\n<p>That&#8217;s not good. The state of the database, according to sys.databases, is RECOVERY_PENDING. Restart recovery has not run and cannot run because the log file&#8217;s missing. The error log says the following<\/p>\n<blockquote><p>Error: 17207, Severity: 16, State: 1.<\/p>\n<p>FileMgr::StartLogFiles: Operating system error 2(The system cannot find the file specified.) occurred while creating or opening file &#8216;D:\\Develop\\Databases\\TestingLogDeletion.ldf&#8217;. Diagnose and correct the operating system error, and retry the operation.<\/p>\n<p>File activation failure. The physical file name &#8220;D:\\Develop\\Databases\\TestingLogDeletion.ldf&#8221; may be incorrect.<\/p>\n<p>The log cannot be rebuilt because the database was not cleanly shut down.<\/p><\/blockquote>\n<p>There are two ways to fix this. Restore from backup or attempt a log rebuild and repair. If there is a current database backup, the first option is by far the best one. The repair should be a last resort, for when there is no backup.<\/p>\n<p>In this case, for demonstration purposes, I&#8217;m going to pretend there&#8217;s no backup and go for a repair. This technique is given in more detail on Paul Randal&#8217;s blog &#8211; <a href=\"http:\/\/www.sqlskills.com\/BLOGS\/PAUL\/post\/Search-Engine-QA-4-Using-EMERGENCY-mode-to-access-a-RECOVERY-PENDING-or-SUSPECT-database.aspx\">Using EMERGENCY mode to access a RECOVERY PENDING or SUSPECT database<\/a><\/p>\n<p><strong>Step 1 &#8211; Set the database into Emergency mode<\/strong><\/p>\n<p>Fortunately in 2005 and later, using Emergency mode is documented and supported and anyone with sysadmin permissions can set a database into Emergency mode.<\/p>\n<p>[source:sql]ALTER DATABASE TestingLogDeletion SET EMERGENCY[\/source]<\/p>\n<p>Now that the DB is in Emergency mode I can access it again. So what&#8217;s in that table that I was populating? Remember that the highest identity value before the shutdown and log deletion was 4778.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-252\" title=\"records\" src=\"https:\/\/www.sqlinthewild.co.za\/wp-content\/uploads\/2009\/06\/records.png\" alt=\"\" width=\"435\" height=\"131\" \/><\/p>\n<p>I&#8217;ve lost almost 3300 records. Imagine if this was a sales system, or stock market trading or something similar.<\/p>\n<p>Because the DB was not shut down cleanly, all the changed data pages were not written to disk. Normally those changes would be replayed from the transaction log during the restart-recovery but because the log was deleted and restart-recovery can&#8217;t run, those changes have been lost, and there is no way to get them back.<\/p>\n<p><strong>Step 2 &#8211; Emergency mode repair<\/strong><\/p>\n<p>To bring the DB back online I need to run a repair. This will fix any structural problems caused by the missing log and bring the database online.<\/p>\n<p>[source:sql]ALTER DATABASE TestingLogDeletion SET SINGLE_USER<br \/>\nDBCC CHECKDB(TestingLogDeletion, REPAIR_ALLOW_DATA_LOSS)[\/source]<\/p>\n<p>CheckDB complained about the missing log then went and rebuilt the log with a whole bunch of extra warnings<\/p>\n<blockquote><p>File activation failure. The physical file name &#8220;D:\\Develop\\Databases\\TestingLogDeletion.ldf&#8221; may be incorrect.<\/p>\n<p>The log cannot be rebuilt because the database was not cleanly shut down.<\/p>\n<p>Warning: The log for database &#8216;TestingLogDeletion&#8217; has been rebuilt. Transactional consistency has been lost. The RESTORE chain was broken, and the server no longer has context on the previous log files, so you will need to know what they were. You should run DBCC CHECKDB to validate physical consistency. The database has been put in dbo-only mode. When you are ready to make the database available for use, you will need to reset database options and delete any extra log files.<\/p>\n<p>DBCC results for &#8216;TestingLogDeletion&#8217;.<br \/>\nCHECKDB found 0 allocation errors and 0 consistency errors in database &#8216;TestingLogDeletion&#8217;.<\/p><\/blockquote>\n<p>In this case there was no corruption. That will not always be the case. Because recovery acts on both user data and the DB&#8217;s structure there may be physical corruption that has to be repaired. It&#8217;s even possible that there&#8217;s irreparable corruption to system tables or allocation pages and if Emergency mode repair fails there&#8217;s no other way to bring the DB back online<\/p>\n<p>After running repair if I check sys.databases the state of the database is Online, so I can bring it out of restricted user mode and it&#8217;s usable again.<\/p>\n<p>Now that was bad enough. Lost data and a database that was unavailable for however long CheckDB takes to repair, and on big databases that could be many hours. Let&#8217;s see how bad things are when I detach the database and then delete the log file.<\/p>\n<p>Same setup, but instead of shutting SQL down, I&#8217;ll detach the database.<\/p>\n<p>Interesting point, if I do the detach through Object Explorer the detach dialog comes back with an error saying that detach failed because the transaction log is full. However the detach actually succeeds, the database is not longer there.<\/p>\n<p>So, DB detached. Now I&#8217;m going to delete the log and use the CREATE DATABASE syntax to reattach it.<\/p>\n<p>[source:sql]CREATE DATABASE TestingLogDeletion<br \/>\nON (FILENAME = &#8216;D:\\Develop\\Databases\\TestingLogDeletion.mdf&#8217;)<br \/>\nFOR ATTACH_REBUILD_LOG [\/source]<\/p>\n<p>That should work fine, right. Right?<\/p>\n<p>Wrong.<\/p>\n<blockquote><p>File activation failure. The physical file name &#8220;D:\\Develop\\Databases\\TestingLogDeletion.ldf&#8221; may be incorrect.<br \/>\nThe log cannot be rebuilt because the database was not cleanly shut down.<br \/>\n<span style=\"color: #ff0000;\">Msg 1813, Level 16, State 2, Line 1<br \/>\nCould not open new database &#8216;TestingLogDeletion&#8217;. CREATE DATABASE is aborted.<\/span><\/p><\/blockquote>\n<p>Much the same as the error that we got when stopping SQL, deleting the log file and restarting SQL but with one very big difference. I can&#8217;t set the DB into emergency mode and run repair. The attach failed, the database is not there.<\/p>\n<p>So this is a bit of a catch-22. I can&#8217;t rebuild the log because the database isn&#8217;t attached and I can&#8217;t attach because the log is missing. Nasty situation. Same as in the first case, the recommended approach here is to restore backups (and we all have backups, right?). If there are no backups, there is a way to hack the DB back into the server, but it isn&#8217;t pretty.<\/p>\n<p>To get that DB back into SQL I need to create a new database with the same number of files and the same (as much as possible) size of files.<\/p>\n<p>[source:sql]CREATE DATABASE TestingLogDeletion_2<br \/>\nON (NAME = Test_dat,<br \/>\nFILENAME = &#8216;D:\\Develop\\Databases\\TestingLogDeletion_2.mdf&#8217;,<br \/>\nSIZE = 10,<br \/>\nMAXSIZE = UNLIMITED,<br \/>\nFILEGROWTH = 5 )<br \/>\nLOG ON ( NAME = Test_log,<br \/>\nFILENAME = &#8216;D:\\Develop\\Databases\\TestingLogDeletion_2.ldf&#8217;,<br \/>\nSIZE = 5MB,<br \/>\nMAXSIZE = 5MB);<br \/>\nGO[\/source]<\/p>\n<p>Then stop SQL, delete both of the files of the newly created database and rename the mdf file of the database I detached earlier to match the mdf file of this DB. Once that&#8217;s done, restart SQL. When SQL starts it will find that the log file is missing and the DB not shut down cleanly and it will put the DB into the RECOVERY_PENDING state. From here the steps to get the DB back online are the same as in the first case. Set it into Emergency mode and run CheckDB with a repair option. Again there will be downtime while checkDB repairs and rebuilds the log, there may be some data loss and there&#8217;s always the chance that emergency mode repair will fail.<\/p>\n<p>So, in conclusion. Don&#8217;t delete the transaction log. Sometimes SQL can rebuild it without too many problems, sometimes it can&#8217;t.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Or &#8220;Why is my database now inaccessible?&#8221; This is another things that I&#8217;ve seen a few things recently on various forums. A DB&#8217;s transaction log fills the disk, probably due to a lack of log backups, and then either SQL&#8230; <a class=\"read-more-button\" href=\"https:\/\/www.sqlinthewild.co.za\/index.php\/2009\/06\/09\/deleting-the-transaction-log\/\">(Read more)<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[20,22,15,16],"tags":[],"class_list":["post-250","post","type-post","status-publish","format-standard","hentry","category-admin","category-corruption","category-sql-server","category-syndication"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p7h6n-42","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/posts\/250","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/comments?post=250"}],"version-history":[{"count":0,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/posts\/250\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/media?parent=250"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/categories?post=250"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/tags?post=250"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}