]> err.no Git - libchamplain/commitdiff
Implement cache purging
authorPierre-Luc Beaudoin <pierre-luc@pierlux.com>
Fri, 1 May 2009 23:08:08 +0000 (19:08 -0400)
committerPierre-Luc Beaudoin <pierre-luc@pierlux.com>
Fri, 1 May 2009 23:08:08 +0000 (19:08 -0400)
champlain/champlain-cache.c
champlain/champlain-cache.h
champlain/champlain-network-map-source.c
champlain/champlain.h

index 92ccf3768d5d6513ce86cb26159e5234b04e777c..ecc4c60a7bb8343bd2f1f7f7d578fc59a28cdc9e 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <glib.h>
 #include <gio/gio.h>
+#include <string.h>
 #include <sqlite3.h>
 
 G_DEFINE_TYPE (ChamplainCache, champlain_cache, G_TYPE_OBJECT)
@@ -46,6 +47,10 @@ struct _ChamplainCachePrivate {
   sqlite3 *data;
 };
 
+static void
+inc_popularity (ChamplainCache *self,
+    ChamplainTile *tile);
+
 static void
 champlain_cache_get_property (GObject *object,
                              guint property_id,
@@ -134,7 +139,7 @@ champlain_cache_init (ChamplainCache *self)
   filename = g_build_filename (g_get_user_cache_dir (), "champlain",
       "cache.db", NULL);
 
-  champlain_cache_set_size_limit (self, 10);
+  champlain_cache_set_size_limit (self, 100000000);
 
   error = sqlite3_open_v2 (filename, &priv->data,
       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
@@ -144,7 +149,7 @@ champlain_cache_init (ChamplainCache *self)
       goto cleanup;
     }
 
-  sqlite3_exec (priv->data, "CREATE TABLE etags (filename char(500) PRIMARY KEY, etag char (30))", NULL, NULL, &error_msg);
+  sqlite3_exec (priv->data, "CREATE TABLE tiles (filename char(500) PRIMARY KEY, etag CHAR (30), popularity INT DEFAULT 1, size INT DEFAULT 0)", NULL, NULL, &error_msg);
   if (error_msg != NULL)
     {
       DEBUG ("Creating table Etag failed: %s", error_msg);
@@ -184,7 +189,7 @@ void
 champlain_cache_set_size_limit (ChamplainCache *self,
     guint size_limit)
 {
-  g_return_if_fail(CHAMPLAIN_CACHE (self));
+  g_return_if_fail (CHAMPLAIN_CACHE (self));
 
   ChamplainCachePrivate *priv = GET_PRIVATE (self);
 
@@ -233,7 +238,7 @@ champlain_cache_fill_tile (ChamplainCache *self,
   champlain_tile_set_modified_time (tile, modified_time);
 
   /* Retrieve etag */
-  query = g_strdup_printf ("SELECT etag FROM etags WHERE filename = '%s'",
+  query = g_strdup_printf ("SELECT etag FROM tiles WHERE filename = '%s'",
       filename);
   sqlite3_exec (priv->data, query, set_etag, tile, &error_msg);
   if (error_msg != NULL)
@@ -250,6 +255,8 @@ champlain_cache_fill_tile (ChamplainCache *self,
   g_object_unref (info);
   g_free (query);
 
+  inc_popularity (self, tile);
+
   return TRUE;
 }
 
@@ -272,25 +279,152 @@ champlain_cache_tile_is_expired (ChamplainCache *self,
   return validate_cache;
 }
 
+static void
+inc_popularity (ChamplainCache *self,
+    ChamplainTile *tile)
+{
+  g_return_if_fail (CHAMPLAIN_CACHE (self));
+  gchar *query, *error = NULL;
+
+  ChamplainCachePrivate *priv = GET_PRIVATE (self);
+
+  query = g_strdup_printf ("UPDATE tiles SET popularity = popularity + 1 WHERE filename = '%s';",
+      champlain_tile_get_filename (tile));
+  sqlite3_exec (priv->data, query, NULL, NULL, &error);
+  if (error != NULL)
+    {
+      DEBUG ("Updating popularity failed: %s", error);
+      sqlite3_free (error);
+    }
+  g_free (query);
+}
+
+static void
+delete_tile (ChamplainCache *self,
+    const gchar *filename)
+{
+  g_return_if_fail (CHAMPLAIN_CACHE (self));
+  gchar *query, *error = NULL;
+  GError *gerror;
+  GFile *file;
+
+  ChamplainCachePrivate *priv = GET_PRIVATE (self);
+
+  query = g_strdup_printf ("DELETE FROM tiles WHERE filename = '%s';", filename);
+  sqlite3_exec (priv->data, query, NULL, NULL, &error);
+  if (error != NULL)
+    {
+      DEBUG ("Deleting tile from db failed: %s", error);
+      sqlite3_free (error);
+    }
+  g_free (query);
+
+  if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+    return;
+
+  file = g_file_new_for_path (filename);
+  if (!g_file_delete (file, NULL, &gerror))
+    {
+        DEBUG ("Deleting tile from disk failed: %s", gerror->message);
+        g_error_free (gerror);
+    }
+  g_object_unref (file);
+}
+
 void
 champlain_cache_update_tile (ChamplainCache *self,
-    ChamplainTile *tile)
+    ChamplainTile *tile,
+    guint size)
 {
-  g_return_if_fail(CHAMPLAIN_CACHE (self));
+  g_return_if_fail (CHAMPLAIN_CACHE (self));
   gchar *query, *error = NULL;
 
   ChamplainCachePrivate *priv = GET_PRIVATE (self);
 
-  query = g_strdup_printf ("REPLACE INTO etags (filename, etag) VALUES ('%s', '%s');",
+  query = g_strdup_printf ("REPLACE INTO tiles (filename, etag, size) VALUES ('%s', '%s', %d);",
       champlain_tile_get_filename (tile),
-      champlain_tile_get_etag (tile));
+      champlain_tile_get_etag (tile),
+      size);
   sqlite3_exec (priv->data, query, NULL, NULL, &error);
   if (error != NULL)
     {
-      DEBUG ("Saving Etag failed: %s", error);
+      DEBUG ("Saving Etag and size failed: %s", error);
       sqlite3_free (error);
     }
+  g_free (query);
+}
+
+static gboolean
+purge_on_idle (gpointer data)
+{
+  champlain_cache_purge (CHAMPLAIN_CACHE (data));
+  return FALSE;
+}
+
+void
+champlain_cache_purge_on_idle (ChamplainCache *self)
+{
+  g_return_if_fail (CHAMPLAIN_CACHE (self));
+  g_idle_add (purge_on_idle, self);
+}
+
+void
+champlain_cache_purge (ChamplainCache *self)
+{
+  g_return_if_fail (CHAMPLAIN_CACHE (self));
+
+  ChamplainCachePrivate *priv = GET_PRIVATE (self);
+  gchar *query;
+  sqlite3_stmt *stmt;
+  int rc = 0;
+  guint current_size = 0;
+
+  query = g_strdup_printf ("SELECT SUM (size) FROM tiles;");
+  rc = sqlite3_prepare (priv->data, query, strlen (query), &stmt, NULL);
+  if (rc != SQLITE_OK)
+    {
+      DEBUG ("Can't compute cache size %s", sqlite3_errmsg(priv->data));
+    }
+  g_free (query);
+
+  rc = sqlite3_step (stmt);
+  current_size = sqlite3_column_int (stmt, 0);
+  if (current_size < priv->size_limit)
+    {
+      DEBUG ("Cache doesn't need to be purged at %d bytes", current_size);
+      sqlite3_finalize (stmt);
+      return;
+    }
+
+  sqlite3_finalize (stmt);
+
+  /* Ok, delete the less popular tiles until size_limit reached */
+  query = g_strdup_printf ("SELECT filename, size FROM tiles ORDER BY popularity;");
+  rc = sqlite3_prepare (priv->data, query, strlen (query), &stmt, NULL);
+  if (rc != SQLITE_OK)
+    {
+      DEBUG ("Can't fetch tiles to delete: %s", sqlite3_errmsg(priv->data));
+    }
+
+  rc = sqlite3_step (stmt);
+  while (rc == SQLITE_ROW && current_size > priv->size_limit)
+    {
+      const char *filename = sqlite3_column_text (stmt, 0);
+      guint size;
+
+      filename = sqlite3_column_text (stmt, 0);
+      size = sqlite3_column_int (stmt, 1);
+      DEBUG ("Deleting %s of size %d", filename, size);
 
+      delete_tile (self, filename);
 
+      current_size -= size;
+
+      rc = sqlite3_step (stmt);
+    }
+  DEBUG ("Cache size is now %d", current_size);
+
+  sqlite3_finalize (stmt);
   g_free (query);
 }
+
index 6fbe7c43a40cc7b7e8184aaf8b66073c1c248d2d..fe0d01e59450e7266bc317cf5591b2bc5f6389ab 100644 (file)
@@ -57,7 +57,8 @@ GType champlain_cache_get_type (void);
 ChamplainCache* champlain_cache_get_default (void);
 
 void champlain_cache_update_tile (ChamplainCache *self,
-    ChamplainTile *tile);
+    ChamplainTile *tile,
+    guint filesize);
 gboolean champlain_cache_fill_tile (ChamplainCache *self,
     ChamplainTile *tile);
 gboolean champlain_cache_tile_is_expired (ChamplainCache *self,
@@ -68,6 +69,7 @@ void champlain_cache_set_size_limit (ChamplainCache *self,
 guint champlain_cache_get_size_limit (ChamplainCache *self);
 
 void champlain_cache_purge (ChamplainCache *self);
+void champlain_cache_purge_on_idle (ChamplainCache *self);
 
 G_END_DECLS
 
index 4e17e8dfcbd4bb5b299b2bc31a1653eb52f61fe2..27359ef8835f295e344403a0399b831cadef8926 100644 (file)
@@ -36,7 +36,7 @@
 #include <gdk/gdk.h>
 #include <gio/gio.h>
 #include <glib.h>
-#include <glib/gprintf.h>
+#include <glib/gstdio.h>
 #include <glib-object.h>
 #ifdef HAVE_LIBSOUP_GNOME
 #include <libsoup/soup-gnome.h>
@@ -44,6 +44,7 @@
 #include <libsoup/soup.h>
 #endif
 #include <math.h>
+#include <sys/stat.h>
 #include <string.h>
 #include <clutter-cairo.h>
 
@@ -414,6 +415,7 @@ file_loaded_cb (SoupSession *session,
   ClutterActor *actor;
   GFile *file;
   GFileInfo *info;
+  guint filesize = 0;
   ChamplainCache *cache = champlain_cache_get_default ();
 
   filename = champlain_tile_get_filename (ctx->tile);
@@ -494,8 +496,13 @@ file_loaded_cb (SoupSession *session,
     }
 
   /* Write the cache */
-  g_file_set_contents (filename, msg->response_body->data,
-      msg->response_body->length, NULL);
+  if (g_file_set_contents (filename, msg->response_body->data,
+      msg->response_body->length, NULL))
+    {
+      struct stat info;
+      g_stat (filename, &info);
+      filesize = info.st_size;
+    }
 
   /* Verify if the server sent an etag and save it */
   const gchar *etag = soup_message_headers_get (msg->response_headers, "ETag");
@@ -505,7 +512,7 @@ file_loaded_cb (SoupSession *session,
     {
       champlain_tile_set_etag (ctx->tile, etag);
     }
-  champlain_cache_update_tile (cache, ctx->tile);
+  champlain_cache_update_tile (cache, ctx->tile, filesize); //XXX
 
   /* Load the image into clutter */
   GdkPixbuf* pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
index 75abce45b2de90b9d1dca9c8267204cacd34df6e..83d615e05d6957f99cdc706e81cf86a7400fb2bf 100644 (file)
@@ -29,6 +29,7 @@
 #include <glib.h>
 
 #include "champlain/champlain-defines.h"
+#include "champlain/champlain-cache.h"
 #include "champlain/champlain-layer.h"
 #include "champlain/champlain-base-marker.h"
 #include "champlain/champlain-marker.h"