{"id":890,"date":"2009-04-21T21:29:00","date_gmt":"2009-04-21T21:29:00","guid":{"rendered":"\/blogs\/paul\/post\/FILESTREAM-garbage-collection.aspx"},"modified":"2017-06-22T06:11:04","modified_gmt":"2017-06-22T13:11:04","slug":"filestream-garbage-collection","status":"publish","type":"post","link":"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/","title":{"rendered":"FILESTREAM garbage collection"},"content":{"rendered":"<p style=\"text-align: justify;\">In my previous posts on FILESTREAM I discussed the <a href=\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-directory-structure\/\">directory structure of the FILESTREAM data container<\/a>\u00a0and <a href=\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-directory-structure-where-do-the-guids-come-from\/\">how to map the directories to database tables and columns<\/a>. In this post I&#8217;m going to explain how and when the FILESTREAM garbage collection process works as that doesn&#8217;t seem to be documented anywhere (even in the <a href=\"https:\/\/www.sqlskills.com\/blogs\/paul\/sql-server-2008-filestream-whitepaper-published-on-msdn\/\">FILESTREAM whitepaper<\/a> I wrote for MS &#8211; it wasn&#8217;t supposed to be that low-level). There seems to be a lot of confusion about how updates of FILESTREAM data work, and when the old versions of the FILESTREAM files are removed. I&#8217;m going to explain how it all works and then show you by example.<\/p>\n<p style=\"text-align: justify;\">The basic behavior that is non-intuitive is that there&#8217;s no such thing as a partial update of FILESTREAM data. If you have 10MB of data stored in a FILESTREAM column (and hence have a 10MB FILESTREAM file), then updating even a single byte of it will result in a whole new 10MB FILESTREAM file. Anything that relies on having an up-to-date version of the database (e.g. log backups, log-shipping, replication) will pick up the entire new 10MB FILESTREAM file. Every time an update is made to that data, a new 10MB FILESTREAM file is created and then subsequently backed-up, replicated, etc. This can lead to unexpectedly large log backups, or network traffic between replication nodes.<\/p>\n<p style=\"text-align: justify;\">Once you realize that new versions of the FILESTREAM files are going to be created, the obvious follow-on question is: when do the old versions get removed? The answer is: it depends!<\/p>\n<p style=\"text-align: justify;\">The old versions are removed by a process called\u00a0garbage collection &#8211; in much the same way that memory garbage collection runs\u00a0for managed code and deallocates object instantiations that are no longer referenced by any variables. The\u00a0key point is that nothing needs the object instantiation any more; otherwise the memory garbage collection would be corrupting the run-time memory of the managed code application. The same principle applies for FILESTREAM garbage collection\u00a0&#8211; the old versions of the FILESTREAM files cannot be removed until they are <em>no longer needed<\/em>.<\/p>\n<p style=\"text-align: justify;\">But what does\u00a0&#8216;no longer needed&#8217; mean for FILESTREAM files? Well, it&#8217;s kind\u00a0of the same as for transaction log records. An old version of a\u00a0FILESTREAM\u00a0file is no longer needed if the transaction that created it has committed or rolled back,\u00a0AND there are no other technologies that must read it, like a log backup (when running in the <em>FULL<\/em> or <em>BULK_LOGGED<\/em> recovery models), or the transactional replication log reader. In fact, the transaction log VLF containing the log record of the creation of the FILESTREAM data file must be switched to inactive before the\u00a0FILESTREAM file can\u00a0be garbage collected.\u00a0Note that I don&#8217;t mention database mirroring &#8211; in SQL 2008 database mirroring and FILESTREAM cannot be used together.<\/p>\n<p style=\"text-align: justify;\">Once the old FILESTREAM file is no longer needed, it is available for garbage collection. How does the garbage collection process know which FILESTREAM files to physically delete? The answer is that when the file is no longer needed, an entry is made in a special table called a &#8216;tombstone&#8217; table. The garbage collection process scans the tombstone tables and removes only the FILESTREAM files with an entry in the tombstone table. You can read more about the tombstone tables in <a href=\"http:\/\/blogs.msdn.com\/b\/psssql\/archive\/2008\/01\/15\/how-it-works-file-stream-the-before-and-after-image-of-a-file.aspx\">this blog post<\/a> from the CSS blog.<\/p>\n<p style=\"text-align: justify;\">So when does the garbage collection process actually run? It can&#8217;t be part of log backups, because in the <em>SIMPLE<\/em> recovery model, you can&#8217;t take log backups. The answer is that it runs as part of the database checkpoint process. This is what causes some confusion &#8211; an old FILESTREAM file will not be removed until after it is no longer needed AND a checkpoint runs.<\/p>\n<p style=\"text-align: justify;\">Now let&#8217;s see this stuff in action. I&#8217;m going to create a database with FILESTREAM data in and then play around with transactions, log backups, and checkpoints to show you garbage collection working.<\/p>\n<pre class=\"brush: sql; title: ; toolbar: true; wrap-lines: true; notranslate\" title=\"\">\r\nCREATE DATABASE &#x5B;FileStreamTestDB] ON PRIMARY\r\n    (NAME = &#x5B;FileStreamTestDB_data],\r\n    FILENAME = N'C:\\Metro Demos\\FileStreamTestDB\\FSTestDB_data.mdf'),\r\nFILEGROUP &#x5B;FileStreamFileGroup] CONTAINS FILESTREAM\r\n    (NAME = &#x5B;FileStreamTestDBDocuments],\r\n    FILENAME = N'C:\\Metro Demos\\FileStreamTestDB\\Documents')\r\nLOG ON\r\n    (NAME = &#x5B;FileStreamTestDB_log],\r\n    FILENAME = N'C:\\Metro Demos\\FileStreamTestDB\\FSTestDB_log.ldf');\r\nGO\r\n\r\nUSE &#x5B;FileStreamTestDB];\r\nGO\r\n\r\nCREATE TABLE &#x5B;FileStreamTest1] (\r\n    &#x5B;DocId] UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL UNIQUE,\r\n    &#x5B;DocName] VARCHAR (25),\r\n    &#x5B;Document] VARBINARY(MAX) FILESTREAM);\r\nGO\r\n<\/pre>\n<p style=\"text-align: justify;\">Now I&#8217;m going to put the database into the <em>FULL<\/em> recovery model and take a full database backup &#8211; which means I must now take log backups to manage the size of the transaction log. It also means that a FILESTREAM file cannot be removed until it has been backed up.<\/p>\n<pre class=\"brush: sql; title: ; toolbar: true; wrap-lines: true; notranslate\" title=\"\">\r\nALTER DATABASE &#x5B;FileStreamTestDB] SET RECOVERY FULL;\r\nGO\r\nBACKUP DATABASE &#x5B;FileStreamTestDB] TO DISK = N'C:\\SQLskills\\FSTDB.bak';\r\nGO\r\n<\/pre>\n<p>Now I&#8217;m going to create some FILESTREAM data.<\/p>\n<pre class=\"brush: sql; title: ; toolbar: true; wrap-lines: true; notranslate\" title=\"\">\r\nINSERT INTO &#x5B;FileStreamTest1] VALUES (\r\n    NEWID (),\r\n    'Paul Randal',\r\n    CAST ('SQLskills.com' AS VARBINARY(MAX)));\r\nGO\r\n<\/pre>\n<p>Looking in the FILESTREAM data container I created, I have the following\u00a0file:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-content\/uploads\/2009\/4\/fsgctest1.jpg\" alt=\"\" width=\"290\" height=\"66\" \/><\/p>\n<p style=\"text-align: justify;\">Remember from the previous blog posts, that the FILESTREAM file filenames are the database log sequence number at the time they were created. Now I&#8217;ll update the value in an implicit transaction (no <em>BEGIN TRAN<\/em> and <em>COMMIT TRAN<\/em>).<\/p>\n<pre class=\"brush: sql; title: ; toolbar: true; wrap-lines: true; notranslate\" title=\"\">\r\nUPDATE &#x5B;FileStreamTest1]\r\n    SET &#x5B;Document] = CAST (REPLICATE ('Paul', 2000) AS VARBINARY(MAX))\r\nWHERE &#x5B;DocName] LIKE '%Randal%';\r\nGO\r\n<\/pre>\n<p>And we now have the following files:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-content\/uploads\/2009\/4\/fsgctest2.jpg\" alt=\"\" width=\"280\" height=\"78\" \/><\/p>\n<p style=\"text-align: justify;\">The new file is the 8KB file and\u00a0the old FILESTREAM value\u00a0is the 1KB file.\u00a0If I try doing an explicit <em>CHECKPOINT<\/em>, nothing changes as the old file is still required as it hasn&#8217;t yet been backed up. Now I&#8217;ll do a log backup.<\/p>\n<pre class=\"brush: sql; title: ; toolbar: true; wrap-lines: true; notranslate\" title=\"\">\r\nBACKUP LOG &#x5B;FileStreamTestDB] TO DISK = 'C:\\SQLskills\\FSTB_log.bak';\r\nGO\r\n<\/pre>\n<p style=\"text-align: justify;\">And the files are\u00a0all still there. Although the first 1KB file is no longer needed, a\u00a0checkpoint hasn&#8217;t occurred yet, so garbage collection hasn&#8217;t run. Now running an explicit <em>CHECKPOINT<\/em>, the directory still contains the two files. What happened? The transaction log VLF\u00a0containing the log record for the\u00a0creation of the FILESTREAM\u00a0file is still active, so the file is still needed.\u00a0I have to do *another* log backup and checkpoint before garbage collection kicks in (as that will cause the log to cycle, when there&#8217;s nothing happening in the database and no active transactions) and the directory view changes to:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-content\/uploads\/2009\/4\/fsgctest3.jpg\" alt=\"\" width=\"289\" height=\"67\" \/><\/p>\n<p style=\"text-align: justify;\">The alternative would have been to generate more log records, spilling into the next transaction log VLF, then do another log backup which would mark the &#8216;creation&#8217; VLF inactive, and then the next checkpoint would run garbage collection on the file. This, of course, would be the normal course of events in a production database.<\/p>\n<p style=\"text-align: justify;\">So, don&#8217;t get confused if you update a FILESTREAM file, then do a log backup and checkpoint and nothing happens. Remember the transaction log has to have progressed enough for the &#8216;creation&#8217; VLF to be inactive too. You can prove this to yourself by creating an explicit transaction at the same time as the FILESTREAM update (in another, implicit transaction). No matter how many times you backup the log and checkpoint the database, the garbage collection will not run until the explicit transaction is committed or rolled back, and then another log backup and checkpoint is run.<\/p>\n<p style=\"text-align: justify;\">I&#8217;ll leave it as a fun exercise for you to play around with updates in explicit transactions and various backup scenarios to see when garbage collection can and cannot remove old files, but now you know exactly how it works.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my previous posts on FILESTREAM I discussed the directory structure of the FILESTREAM data container\u00a0and how to map the directories to database tables and columns. In this post I&#8217;m going to explain how and when the FILESTREAM garbage collection process works as that doesn&#8217;t seem to be documented anywhere (even in the FILESTREAM whitepaper [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[41,48,86],"tags":[],"class_list":["post-890","post","type-post","status-publish","format-standard","hentry","category-filestream","category-inside-the-storage-engine","category-sql-server-2008"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>FILESTREAM garbage collection - Paul S. Randal<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"FILESTREAM garbage collection - Paul S. Randal\" \/>\n<meta property=\"og:description\" content=\"In my previous posts on FILESTREAM I discussed the directory structure of the FILESTREAM data container\u00a0and how to map the directories to database tables and columns. In this post I&#8217;m going to explain how and when the FILESTREAM garbage collection process works as that doesn&#8217;t seem to be documented anywhere (even in the FILESTREAM whitepaper [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/\" \/>\n<meta property=\"og:site_name\" content=\"Paul S. Randal\" \/>\n<meta property=\"article:published_time\" content=\"2009-04-21T21:29:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2017-06-22T13:11:04+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-content\/uploads\/2009\/4\/fsgctest1.jpg\" \/>\n<meta name=\"author\" content=\"Paul Randal\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Paul Randal\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/\",\"name\":\"FILESTREAM garbage collection - Paul S. Randal\",\"isPartOf\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-content\/uploads\/2009\/4\/fsgctest1.jpg\",\"datePublished\":\"2009-04-21T21:29:00+00:00\",\"dateModified\":\"2017-06-22T13:11:04+00:00\",\"author\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/#primaryimage\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-content\/uploads\/2009\/4\/fsgctest1.jpg\",\"contentUrl\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-content\/uploads\/2009\/4\/fsgctest1.jpg\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"FILESTREAM garbage collection\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#website\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/\",\"name\":\"Paul S. Randal\",\"description\":\"In Recovery...\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce\",\"name\":\"Paul Randal\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g\",\"caption\":\"Paul Randal\"},\"sameAs\":[\"http:\/\/3.209.169.194\/blogs\/paul\"],\"url\":\"https:\/\/www.sqlskills.com\/blogs\/paul\/author\/paul\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"FILESTREAM garbage collection - Paul S. Randal","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/","og_locale":"en_US","og_type":"article","og_title":"FILESTREAM garbage collection - Paul S. Randal","og_description":"In my previous posts on FILESTREAM I discussed the directory structure of the FILESTREAM data container\u00a0and how to map the directories to database tables and columns. In this post I&#8217;m going to explain how and when the FILESTREAM garbage collection process works as that doesn&#8217;t seem to be documented anywhere (even in the FILESTREAM whitepaper [&hellip;]","og_url":"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/","og_site_name":"Paul S. Randal","article_published_time":"2009-04-21T21:29:00+00:00","article_modified_time":"2017-06-22T13:11:04+00:00","og_image":[{"url":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-content\/uploads\/2009\/4\/fsgctest1.jpg","type":"","width":"","height":""}],"author":"Paul Randal","twitter_misc":{"Written by":"Paul Randal","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/","url":"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/","name":"FILESTREAM garbage collection - Paul S. Randal","isPartOf":{"@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/#primaryimage"},"image":{"@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/#primaryimage"},"thumbnailUrl":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-content\/uploads\/2009\/4\/fsgctest1.jpg","datePublished":"2009-04-21T21:29:00+00:00","dateModified":"2017-06-22T13:11:04+00:00","author":{"@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce"},"breadcrumb":{"@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/#primaryimage","url":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-content\/uploads\/2009\/4\/fsgctest1.jpg","contentUrl":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-content\/uploads\/2009\/4\/fsgctest1.jpg"},{"@type":"BreadcrumbList","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/filestream-garbage-collection\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.sqlskills.com\/blogs\/paul\/"},{"@type":"ListItem","position":2,"name":"FILESTREAM garbage collection"}]},{"@type":"WebSite","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#website","url":"https:\/\/www.sqlskills.com\/blogs\/paul\/","name":"Paul S. Randal","description":"In Recovery...","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.sqlskills.com\/blogs\/paul\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/ffcec826c18782e1e0adf173826a7fce","name":"Paul Randal","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sqlskills.com\/blogs\/paul\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/0b6a266bba2f088f2551ef529293001bd73bf026bc1908b9866728c062beeeb6?s=96&d=mm&r=g","caption":"Paul Randal"},"sameAs":["http:\/\/3.209.169.194\/blogs\/paul"],"url":"https:\/\/www.sqlskills.com\/blogs\/paul\/author\/paul\/"}]}},"_links":{"self":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/posts\/890","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/comments?post=890"}],"version-history":[{"count":0,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/posts\/890\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/media?parent=890"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/categories?post=890"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/paul\/wp-json\/wp\/v2\/tags?post=890"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}