{
  "attachments": [],
  "comments_archived": true,
  "date": "2003-09-29T17:48:28.000Z",
  "layout": "post",
  "title": "Dynamic polling times for news aggregators, II",
  "wordpress_id": 484,
  "wordpress_slug": "dynamic-polling-freq-too",
  "wordpress_url": "http://www.decafbad.com/blog/?p=484",
  "year": "2003",
  "month": "09",
  "day": "29",
  "isDir": false,
  "slug": "dynamic-polling-freq-too",
  "type": "entry",
  "postName": "2003-09-29-dynamic-polling-freq-too",
  "html": "<p>\nOkay, so that <a href=\"http://www.decafbad.com/blog/tech/dynamic_feed_scan_times.html\">thing with the SQL</a> I did Friday?\nI'm not exactly sure what I was thinking with it.  I was doing something\nthat seems really odd now, trying to collect counts of new items together\nby hour, then averaging those hourly counts across a week.  Instead, I'm\ntrying this now:\n</p>\n\n<pre>SELECT\n  source,\n  'update_period' AS name,\n  round(min(24,max(1,(max(1,(iso8601_to_epoch(max(created)) -\n    max(now() - (7*24*60*60), iso8601_to_epoch(min(created)))) /\n   (60*60))) / count(id))),2) AS value\nFROM\n  items\nWHERE\n  created &gt;= epoch_to_iso8601(now() - (7*24*60*60)) \nGROUP BY\n  source</pre>\n\n<p>\nThis bit of SQL, though still ugly, is much simpler.  This leaves out\nthe subselect, which I think I might have been playing with in order\nto build a little graph display of new items over time by source.  What\nthe above does now is to get an average time between new items for the\npast week, with a minimum of an hour, and a maximum of a day.  This\nseems to be working much better.\n</p>\n\n<p>\nAn alternate algorithm I've been playing with was suggested in\n<a href=\"http://www.decafbad.com/comments/tech/dynamic_feed_scan_times/#comment-aofdehdefioofcb\">a comment</a>\nby <a href=\"http://24.102.209.201/weblogs/ben/\">Gnomon</a>,\ninspired by TCP/IP's Additive Increase / Multiplicative Decrease.\nWith this, I subtract an hour from the time between polls when a\npoll finds new items, and then multiply by 2 every time a poll\ncomes up with nothing new.\n</p>\n\n<p>\nUsing the average of new items over time lessens my pummeling\nof servers per hour, but the second approach is even lighter\non polling since it's biased toward large leaps backing off\nfrom polling when new items are not found.  I'll likely be trading\noff between the two to see which one seems to work best.\n</p>\n\n<p>\nHoping that, after playing a bit, I'll settle on one and my\naggregator will play much nicer with feeds, especially once\nI get the HTTP client usage to correctly use things like\nlast-modified headers and ETags.  There's absolutely no reason\nfor a news aggregator to poll a feed every single hour of a day,\nunless you're monitoring a feed that's mostly quiet, except\nfor emergencies.  In that case, well, a different polling\nalgorithm is needed, or maybe an instant messaging or pub/sub\narchitecture is required.\n</p>\n\n<p>\n<b>Update:</b> As <a href=\"http://24.102.209.201/weblogs/ben/\">Gnomon</a>\nhas corrected me in comments, I've got the AIMD algorithm mixed up.\nWhat I really should be doing is making quick jumps up in polling\nfrequency in response to new items (multiplicative decrease of\npolling period) and creeping away in response to no new items\n(additive increase of polling period).  As he notes, this approach\nshould make an aggregator jump to attention when clumps of new\nposts come in, and gradually get bored over periods of silence.\nI've adjusted my code and will be tinkering with it.\n</p>\n\n<p>\nAlso, although <a href=\"http://24.102.209.201/weblogs/ben/\">Gnomon</a> makes\na good point that bloggers and their posting habits are not easily\nsubject to statistical analysis,\nI've further refined my little SQL query to catch sources\nwhich haven't seen any updates during the week (or ever):\n</p>\n\n<pre>SELECT \n  id as source,\n  'update_period' AS name,\n  round(min(24,max(1,coalesce(update_period,24)))) AS value\nFROM sources\nLEFT JOIN (\n     SELECT\n      source AS source_id,\n            (iso8601_to_epoch(max(created)) -\n              max(\n                now()-(7*24*60*60),\n                iso8601_to_epoch(min(created))\n              )\n            ) / (60*60) / count(id)\n        AS update_period\n    FROM items\n    WHERE created &gt;= epoch_to_iso8601(now() - (7*24*60*60)) \n    GROUP BY source\n) ON sources.id=source_id</pre>\n\n<p>\nAlso, in case anyone's interested, I've checked <a href=\"http://www.decafbad.com/cvs/dbagg/lib/dbagg/scan.py?rev=HEAD&amp;content-type=text/vnd.viewcvs-markup\">all the above</a>\ninto CVS.  This beastie's far from ready for prime time, but it\nmight be interesting to someone.\n</p>\n\n\n<!--more-->\n\n\n<p>shortname=dynamic_polling_freq_too</p>\n<div id=\"comments\" class=\"comments archived-comments\"><h3>Archived Comments</h3>\n<ul class=\"comments\">\n<li class=\"comment\" id=\"comment-221083781\">\n<div class=\"meta\">\n<div class=\"author\">\n<a class=\"avatar image\" rel=\"nofollow\" href=\"http://webseitz.fluxent.com/wiki\"><img src=\"http://www.gravatar.com/avatar.php?gravatar_id=2e83224d92ed7f1148f4dd3cdb0e4548&amp;size=32&amp;default=http://mediacdn.disqus.com/1320279820/images/noavatar32.png\" width=\"\" height=\"\"></a>\n<a class=\"avatar name\" rel=\"nofollow\" href=\"http://webseitz.fluxent.com/wiki\">Bill Seitz</a>\n</div>\n\n\n<p><a href=\"#comment-221083781\" class=\"permalink\"><time datetime=\"2003-09-29T10:11:11\">2003-09-29T10:11:11</time></a></p>\n</div>\n\n\n<div class=\"content\">It might be good to step back and prioritize your goals. How important is quickly catching mid-day updates?\nI think looking at averages throws things off, considering how \"clumpy\" I'd guess most update frequencies are. Some thoughts:\n* probably makes sense to check each feed at least once per day\n* for each feed, look at the average time-of-day of its first-post-of-the-day. Actually, look at a distribution curve, and pick the time at which there's an 80% chance that the first post will have been made (if it's going to be made at all).\n* check at that time; if no posting then check 12 hours later?\n* if found posting at first check of day, then start that additive/multiplicative process\n* regardless of the state of that latter calculation, check the next morning at that time-of-first-post prediction\nParallel idea:\n* calc average time between posts like you're doing, but just over the window each day when posts are being made (e.g. 8am-11pm = 15 hrs, not 24 hrs).</div>\n\n\n</li>\n<li class=\"comment\" id=\"comment-221083784\">\n<div class=\"meta\">\n<div class=\"author\">\n<a class=\"avatar image\" rel=\"nofollow\" href=\"http://www.decafbad.com/blog\"><img src=\"http://www.gravatar.com/avatar.php?gravatar_id=2ac2cffd36ada8c734b90e02a1e5c1ac&amp;size=32&amp;default=http://mediacdn.disqus.com/1320279820/images/noavatar32.png\" width=\"\" height=\"\"></a>\n<a class=\"avatar name\" rel=\"nofollow\" href=\"http://www.decafbad.com/blog\">l.m.orchard</a>\n</div>\n\n\n<p><a href=\"#comment-221083784\" class=\"permalink\"><time datetime=\"2003-09-29T10:39:45\">2003-09-29T10:39:45</time></a></p>\n</div>\n\n\n<div class=\"content\">Ooh, good ideas!  I think a lot of this addresses some of the not-quite-yet thought out concerns I have with the simple averaging.  I'll have to poke around some more with this.</div>\n\n\n</li>\n<li class=\"comment\" id=\"comment-221083786\">\n<div class=\"meta\">\n<div class=\"author\">\n<a class=\"avatar image\" rel=\"nofollow\" href=\"http://24.102.209.201/weblogs/ben/\"><img src=\"http://www.gravatar.com/avatar.php?gravatar_id=588bdfdda82be46c638d6956c55ebc38&amp;size=32&amp;default=http://mediacdn.disqus.com/1320279820/images/noavatar32.png\" width=\"\" height=\"\"></a>\n<a class=\"avatar name\" rel=\"nofollow\" href=\"http://24.102.209.201/weblogs/ben/\">Gnomon</a>\n</div>\n\n\n<p><a href=\"#comment-221083786\" class=\"permalink\"><time datetime=\"2003-09-29T11:18:28\">2003-09-29T11:18:28</time></a></p>\n</div>\n\n\n<div class=\"content\">I'm afraid that you've credited me with more politeness than I deserve! When I suggested AIMD, I meant that the time between polls should be subject to this scheduling system - that is, the poll interval should increase additively but decrease multiplicatively.\nThis is not as polite as your interpretation, which definitely backs off very quickly. My reasoning went like this: weblog posts tend to clump, so the best indicator of an upcoming post is a new post...:\n- If there aren't any new posts, lengthen the check interval by a little bit and check again later; keep lengthening the check interval up to a certain limit.\n- If there is a new post, then it's likely that another new post will follow soon, so substantially decrease the poll time (down to a certain limit) and check again soon.\nThis approach is less biased towards decreasing server load and more biased towards detecting quick clumps of updates, which seem to be the norm. I don't know any human webloggers who have such a predictable posting pattern that they are subject to statistical analysis  ;)</div>\n\n\n</li>\n<li class=\"comment\" id=\"comment-221083787\">\n<div class=\"meta\">\n<div class=\"author\">\n<a class=\"avatar image\" rel=\"nofollow\" href=\"http://www.decafbad.com/blog\"><img src=\"http://www.gravatar.com/avatar.php?gravatar_id=2ac2cffd36ada8c734b90e02a1e5c1ac&amp;size=32&amp;default=http://mediacdn.disqus.com/1320279820/images/noavatar32.png\" width=\"\" height=\"\"></a>\n<a class=\"avatar name\" rel=\"nofollow\" href=\"http://www.decafbad.com/blog\">l.m.orchard</a>\n</div>\n\n\n<p><a href=\"#comment-221083787\" class=\"permalink\"><time datetime=\"2003-09-29T11:53:46\">2003-09-29T11:53:46</time></a></p>\n</div>\n\n\n<div class=\"content\">Oh, duh.  Heh, I've got it in reverse then.  Seems like jumping up / creeping back, now that you've explained it to me again, would better suit the posting styles of bloggers for sure!\nThanks for correcting me!</div>\n\n\n</li>\n<li class=\"comment\" id=\"comment-221083789\">\n<div class=\"meta\">\n<div class=\"author\">\n<a class=\"avatar image\" rel=\"nofollow\" href=\"http://bwinton.latte.ca/\"><img src=\"http://www.gravatar.com/avatar.php?gravatar_id=441f5529f1db69e1a18cefb090e2690a&amp;size=32&amp;default=http://mediacdn.disqus.com/1320279820/images/noavatar32.png\" width=\"\" height=\"\"></a>\n<a class=\"avatar name\" rel=\"nofollow\" href=\"http://bwinton.latte.ca/\">Blake Winton</a>\n</div>\n\n\n<p><a href=\"#comment-221083789\" class=\"permalink\"><time datetime=\"2003-09-29T14:00:39\">2003-09-29T14:00:39</time></a></p>\n</div>\n\n\n<div class=\"content\">I was thinking along the same lines as Bill, in that you know that most webloggers have to sleep and eat sometime, so if you  could take advantage of this knowledge, you'ld be ahead of the game.\nMy thoughts on how to do it would be to break the day into blocks of time (start with hours, say), and build a histogram of how many posts fit into each block.  Then, you could collapse the series of hours where nothing was posted into a big block, and split any hours where something was posted into two sections, to average out the number of posts per hour.  Then, if my theory is correct, you can poll once per block of time, and would have a reasonable chance of getting a new post.\nSome notable flaws with the algorithm:\n1. It fails to account for whole days where there's nothing posted.  This might be overcome by having your initial blocks of time be the days of the week.\n2. It fails to account for the relationship (or the average time) between posts.  So if someone posts a lot at 9:00 or 10:00, but never both at 9:00 and 10:00, my theory will still check the 10:00 time even if something was found at 9:00.  I can't think of a way to get around this, off the top of my head.\nAnother way of thinking of this idea is pre-calculating a guess at the fall-off, based on previous posts.</div>\n\n\n</li>\n</ul>\n\n\n</div>\n\n\n",
  "body": "<p>\r\nOkay, so that <a href=\"http://www.decafbad.com/blog/tech/dynamic_feed_scan_times.html\">thing with the SQL</a> I did Friday?\r\nI'm not exactly sure what I was thinking with it.  I was doing something\r\nthat seems really odd now, trying to collect counts of new items together\r\nby hour, then averaging those hourly counts across a week.  Instead, I'm\r\ntrying this now:\r\n</p>\r\n\r\n<pre>SELECT\r\n  source,\r\n  'update_period' AS name,\r\n  round(min(24,max(1,(max(1,(iso8601_to_epoch(max(created)) -\r\n    max(now() - (7*24*60*60), iso8601_to_epoch(min(created)))) /\r\n   (60*60))) / count(id))),2) AS value\r\nFROM\r\n  items\r\nWHERE\r\n  created >= epoch_to_iso8601(now() - (7*24*60*60)) \r\nGROUP BY\r\n  source</pre>\r\n\r\n<p>\r\nThis bit of SQL, though still ugly, is much simpler.  This leaves out\r\nthe subselect, which I think I might have been playing with in order\r\nto build a little graph display of new items over time by source.  What\r\nthe above does now is to get an average time between new items for the\r\npast week, with a minimum of an hour, and a maximum of a day.  This\r\nseems to be working much better.\r\n</p>\r\n\r\n<p>\r\nAn alternate algorithm I've been playing with was suggested in\r\n<a href=\"http://www.decafbad.com/comments/tech/dynamic_feed_scan_times/#comment-aofdehdefioofcb\">a comment</a>\r\nby <a href=\"http://24.102.209.201/weblogs/ben/\">Gnomon</a>,\r\ninspired by TCP/IP's Additive Increase / Multiplicative Decrease.\r\nWith this, I subtract an hour from the time between polls when a\r\npoll finds new items, and then multiply by 2 every time a poll\r\ncomes up with nothing new.\r\n</p>\r\n\r\n<p>\r\nUsing the average of new items over time lessens my pummeling\r\nof servers per hour, but the second approach is even lighter\r\non polling since it's biased toward large leaps backing off\r\nfrom polling when new items are not found.  I'll likely be trading\r\noff between the two to see which one seems to work best.\r\n</p>\r\n\r\n<p>\r\nHoping that, after playing a bit, I'll settle on one and my\r\naggregator will play much nicer with feeds, especially once\r\nI get the HTTP client usage to correctly use things like\r\nlast-modified headers and ETags.  There's absolutely no reason\r\nfor a news aggregator to poll a feed every single hour of a day,\r\nunless you're monitoring a feed that's mostly quiet, except\r\nfor emergencies.  In that case, well, a different polling\r\nalgorithm is needed, or maybe an instant messaging or pub/sub\r\narchitecture is required.\r\n</p>\r\n\r\n<p>\r\n<b>Update:</b> As <a href=\"http://24.102.209.201/weblogs/ben/\">Gnomon</a>\r\nhas corrected me in comments, I've got the AIMD algorithm mixed up.\r\nWhat I really should be doing is making quick jumps up in polling\r\nfrequency in response to new items (multiplicative decrease of\r\npolling period) and creeping away in response to no new items\r\n(additive increase of polling period).  As he notes, this approach\r\nshould make an aggregator jump to attention when clumps of new\r\nposts come in, and gradually get bored over periods of silence.\r\nI've adjusted my code and will be tinkering with it.\r\n</p>\r\n\r\n<p>\r\nAlso, although <a href=\"http://24.102.209.201/weblogs/ben/\">Gnomon</a> makes\r\na good point that bloggers and their posting habits are not easily\r\nsubject to statistical analysis,\r\nI've further refined my little SQL query to catch sources\r\nwhich haven't seen any updates during the week (or ever):\r\n</p>\r\n\r\n<pre>SELECT \r\n  id as source,\r\n  'update_period' AS name,\r\n  round(min(24,max(1,coalesce(update_period,24)))) AS value\r\nFROM sources\r\nLEFT JOIN (\r\n     SELECT\r\n      source AS source_id,\r\n            (iso8601_to_epoch(max(created)) -\r\n              max(\r\n                now()-(7*24*60*60),\r\n                iso8601_to_epoch(min(created))\r\n              )\r\n            ) / (60*60) / count(id)\r\n        AS update_period\r\n    FROM items\r\n    WHERE created >= epoch_to_iso8601(now() - (7*24*60*60)) \r\n    GROUP BY source\r\n) ON sources.id=source_id</pre>\r\n\r\n<p>\r\nAlso, in case anyone's interested, I've checked <a href=\"http://www.decafbad.com/cvs/dbagg/lib/dbagg/scan.py?rev=HEAD&content-type=text/vnd.viewcvs-markup\">all the above</a>\r\ninto CVS.  This beastie's far from ready for prime time, but it\r\nmight be interesting to someone.\r\n</p>\r\n<!--more-->\r\nshortname=dynamic_polling_freq_too\r\n\r\n<div id=\"comments\" class=\"comments archived-comments\">\r\n            <h3>Archived Comments</h3>\r\n            \r\n        <ul class=\"comments\">\r\n            \r\n        <li class=\"comment\" id=\"comment-221083781\">\r\n            <div class=\"meta\">\r\n                <div class=\"author\">\r\n                    <a class=\"avatar image\" rel=\"nofollow\" \r\n                       href=\"http://webseitz.fluxent.com/wiki\"><img src=\"http://www.gravatar.com/avatar.php?gravatar_id=2e83224d92ed7f1148f4dd3cdb0e4548&amp;size=32&amp;default=http://mediacdn.disqus.com/1320279820/images/noavatar32.png\"/></a>\r\n                    <a class=\"avatar name\" rel=\"nofollow\" \r\n                       href=\"http://webseitz.fluxent.com/wiki\">Bill Seitz</a>\r\n                </div>\r\n                <a href=\"#comment-221083781\" class=\"permalink\"><time datetime=\"2003-09-29T10:11:11\">2003-09-29T10:11:11</time></a>\r\n            </div>\r\n            <div class=\"content\">It might be good to step back and prioritize your goals. How important is quickly catching mid-day updates?\r\n\r\nI think looking at averages throws things off, considering how \"clumpy\" I'd guess most update frequencies are. Some thoughts:\r\n\r\n* probably makes sense to check each feed at least once per day\r\n\r\n* for each feed, look at the average time-of-day of its first-post-of-the-day. Actually, look at a distribution curve, and pick the time at which there's an 80% chance that the first post will have been made (if it's going to be made at all).\r\n\r\n* check at that time; if no posting then check 12 hours later?\r\n\r\n* if found posting at first check of day, then start that additive/multiplicative process\r\n\r\n* regardless of the state of that latter calculation, check the next morning at that time-of-first-post prediction\r\n\r\nParallel idea:\r\n\r\n* calc average time between posts like you're doing, but just over the window each day when posts are being made (e.g. 8am-11pm = 15 hrs, not 24 hrs).</div>\r\n            \r\n        </li>\r\n    \r\n        <li class=\"comment\" id=\"comment-221083784\">\r\n            <div class=\"meta\">\r\n                <div class=\"author\">\r\n                    <a class=\"avatar image\" rel=\"nofollow\" \r\n                       href=\"http://www.decafbad.com/blog\"><img src=\"http://www.gravatar.com/avatar.php?gravatar_id=2ac2cffd36ada8c734b90e02a1e5c1ac&amp;size=32&amp;default=http://mediacdn.disqus.com/1320279820/images/noavatar32.png\"/></a>\r\n                    <a class=\"avatar name\" rel=\"nofollow\" \r\n                       href=\"http://www.decafbad.com/blog\">l.m.orchard</a>\r\n                </div>\r\n                <a href=\"#comment-221083784\" class=\"permalink\"><time datetime=\"2003-09-29T10:39:45\">2003-09-29T10:39:45</time></a>\r\n            </div>\r\n            <div class=\"content\">Ooh, good ideas!  I think a lot of this addresses some of the not-quite-yet thought out concerns I have with the simple averaging.  I'll have to poke around some more with this.</div>\r\n            \r\n        </li>\r\n    \r\n        <li class=\"comment\" id=\"comment-221083786\">\r\n            <div class=\"meta\">\r\n                <div class=\"author\">\r\n                    <a class=\"avatar image\" rel=\"nofollow\" \r\n                       href=\"http://24.102.209.201/weblogs/ben/\"><img src=\"http://www.gravatar.com/avatar.php?gravatar_id=588bdfdda82be46c638d6956c55ebc38&amp;size=32&amp;default=http://mediacdn.disqus.com/1320279820/images/noavatar32.png\"/></a>\r\n                    <a class=\"avatar name\" rel=\"nofollow\" \r\n                       href=\"http://24.102.209.201/weblogs/ben/\">Gnomon</a>\r\n                </div>\r\n                <a href=\"#comment-221083786\" class=\"permalink\"><time datetime=\"2003-09-29T11:18:28\">2003-09-29T11:18:28</time></a>\r\n            </div>\r\n            <div class=\"content\">I'm afraid that you've credited me with more politeness than I deserve! When I suggested AIMD, I meant that the time between polls should be subject to this scheduling system - that is, the poll interval should increase additively but decrease multiplicatively.\r\n\r\nThis is not as polite as your interpretation, which definitely backs off very quickly. My reasoning went like this: weblog posts tend to clump, so the best indicator of an upcoming post is a new post...:\r\n\r\n - If there aren't any new posts, lengthen the check interval by a little bit and check again later; keep lengthening the check interval up to a certain limit.\r\n - If there is a new post, then it's likely that another new post will follow soon, so substantially decrease the poll time (down to a certain limit) and check again soon.\r\n\r\nThis approach is less biased towards decreasing server load and more biased towards detecting quick clumps of updates, which seem to be the norm. I don't know any human webloggers who have such a predictable posting pattern that they are subject to statistical analysis  ;)</div>\r\n            \r\n        </li>\r\n    \r\n        <li class=\"comment\" id=\"comment-221083787\">\r\n            <div class=\"meta\">\r\n                <div class=\"author\">\r\n                    <a class=\"avatar image\" rel=\"nofollow\" \r\n                       href=\"http://www.decafbad.com/blog\"><img src=\"http://www.gravatar.com/avatar.php?gravatar_id=2ac2cffd36ada8c734b90e02a1e5c1ac&amp;size=32&amp;default=http://mediacdn.disqus.com/1320279820/images/noavatar32.png\"/></a>\r\n                    <a class=\"avatar name\" rel=\"nofollow\" \r\n                       href=\"http://www.decafbad.com/blog\">l.m.orchard</a>\r\n                </div>\r\n                <a href=\"#comment-221083787\" class=\"permalink\"><time datetime=\"2003-09-29T11:53:46\">2003-09-29T11:53:46</time></a>\r\n            </div>\r\n            <div class=\"content\">Oh, duh.  Heh, I've got it in reverse then.  Seems like jumping up / creeping back, now that you've explained it to me again, would better suit the posting styles of bloggers for sure!\r\n\r\nThanks for correcting me!</div>\r\n            \r\n        </li>\r\n    \r\n        <li class=\"comment\" id=\"comment-221083789\">\r\n            <div class=\"meta\">\r\n                <div class=\"author\">\r\n                    <a class=\"avatar image\" rel=\"nofollow\" \r\n                       href=\"http://bwinton.latte.ca/\"><img src=\"http://www.gravatar.com/avatar.php?gravatar_id=441f5529f1db69e1a18cefb090e2690a&amp;size=32&amp;default=http://mediacdn.disqus.com/1320279820/images/noavatar32.png\"/></a>\r\n                    <a class=\"avatar name\" rel=\"nofollow\" \r\n                       href=\"http://bwinton.latte.ca/\">Blake Winton</a>\r\n                </div>\r\n                <a href=\"#comment-221083789\" class=\"permalink\"><time datetime=\"2003-09-29T14:00:39\">2003-09-29T14:00:39</time></a>\r\n            </div>\r\n            <div class=\"content\">I was thinking along the same lines as Bill, in that you know that most webloggers have to sleep and eat sometime, so if you  could take advantage of this knowledge, you'ld be ahead of the game.\r\n\r\nMy thoughts on how to do it would be to break the day into blocks of time (start with hours, say), and build a histogram of how many posts fit into each block.  Then, you could collapse the series of hours where nothing was posted into a big block, and split any hours where something was posted into two sections, to average out the number of posts per hour.  Then, if my theory is correct, you can poll once per block of time, and would have a reasonable chance of getting a new post.\r\n\r\nSome notable flaws with the algorithm:\r\n1. It fails to account for whole days where there's nothing posted.  This might be overcome by having your initial blocks of time be the days of the week.\r\n2. It fails to account for the relationship (or the average time) between posts.  So if someone posts a lot at 9:00 or 10:00, but never both at 9:00 and 10:00, my theory will still check the 10:00 time even if something was found at 9:00.  I can't think of a way to get around this, off the top of my head.\r\n\r\nAnother way of thinking of this idea is pre-calculating a guess at the fall-off, based on previous posts.</div>\r\n            \r\n        </li>\r\n    \r\n        </ul>\r\n    \r\n        </div>\r\n    ",
  "parentPath": "./content/posts/archives/2003",
  "path": "2003/09/29/dynamic-polling-freq-too",
  "summary": "Okay, so that thing with the SQL I did Friday?\nI'm not exactly sure what I was thinking with it.  I was doing something\nthat seems really odd now, trying to collect counts of new items together\nby hour, then averaging those hourly counts across a week.  Instead, I'm\ntrying this now:\n\n\nSELECT\n  source,\n  'update_period' AS name,\n  round(min(24,max(1,(max(1,(iso8601_to_epoch(max(created)) -\n    max(now() - (7*24*60*60), iso8601_to_epoch(min(created)))) /\n   (60*60))) / count(id))),2) AS value\nFROM\n  items\nWHERE\n  created >= epoch_to_iso8601(now() - (7*24*60*60)) \nGROUP BY\n  source\n\n\nThis bit of SQL, though still ugly, is much simpler.  This leaves out\nthe subselect, which I think I might have been playing with in order\nto build a little graph display of new items over time by source.  What\nthe above does now is to get an average time between new items for the\npast week, with a minimum of an hour, and a maximum of a day.  This\nseems to be working much better.\n\n\n\nAn alternate algorithm I've been playing with was suggested in\na comment\nby Gnomon,\ninspired by TCP/IP's Additive Increase / Multiplicative Decrease.\nWith this, I subtract an hour from the time between polls when a\npoll finds new items, and then multiply by 2 every time a poll\ncomes up with nothing new.\n\n\n\nUsing the average of new items over time lessens my pummeling\nof servers per hour, but the second approach is even lighter\non polling since it's biased toward large leaps backing off\nfrom polling when new items are not found.  I'll likely be trading\noff between the two to see which one seems to work best.\n\n\n\nHoping that, after playing a bit, I'll settle on one and my\naggregator will play much nicer with feeds, especially once\nI get the HTTP client usage to correctly use things like\nlast-modified headers and ETags.  There's absolutely no reason\nfor a news aggregator to poll a feed every single hour of a day,\nunless you're monitoring a feed that's mostly quiet, except\nfor emergencies.  In that case, well, a different polling\nalgorithm is needed, or maybe an instant messaging or pub/sub\narchitecture is required.\n\n\n\nUpdate: As Gnomon\nhas corrected me in comments, I've got the AIMD algorithm mixed up.\nWhat I really should be doing is making quick jumps up in polling\nfrequency in response to new items (multiplicative decrease of\npolling period) and creeping away in response to no new items\n(additive increase of polling period).  As he notes, this approach\nshould make an aggregator jump to attention when clumps of new\nposts come in, and gradually get bored over periods of silence.\nI've adjusted my code and will be tinkering with it.\n\n\n\nAlso, although Gnomon makes\na good point that bloggers and their posting habits are not easily\nsubject to statistical analysis,\nI've further refined my little SQL query to catch sources\nwhich haven't seen any updates during the week (or ever):\n\n\nSELECT \n  id as source,\n  'update_period' AS name,\n  round(min(24,max(1,coalesce(update_period,24)))) AS value\nFROM sources\nLEFT JOIN (\n     SELECT\n      source AS source_id,\n            (iso8601_to_epoch(max(created)) -\n              max(\n                now()-(7*24*60*60),\n                iso8601_to_epoch(min(created))\n              )\n            ) / (60*60) / count(id)\n        AS update_period\n    FROM items\n    WHERE created >= epoch_to_iso8601(now() - (7*24*60*60)) \n    GROUP BY source\n) ON sources.id=source_id\n\n\nAlso, in case anyone's interested, I've checked all the above\ninto CVS.  This beastie's far from ready for prime time, but it\nmight be interesting to someone.",
  "needsBuild": true,
  "prevPostPath": "2003/09/25/dynamic-feed-scan-times",
  "prevPostTitle": "Dynamic feed polling times for news aggregators",
  "nextPostPath": "2003/10/01/mailbucket-feeds",
  "nextPostTitle": "Mailbucket syndication feeds"
}