changeset 1143:b4e260696193

post: keep track of all files used during post loading If we know which files are needed, we can check whether or not they have been modified and therefore whether or not we need to actually refresh the post. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Tue, 13 Nov 2018 20:50:59 -0500
parents 4493f328dc55
children 353959273635
files listing.c post.c post.h
diffstat 3 files changed, 128 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/listing.c	Tue Nov 13 16:46:32 2018 -0500
+++ b/listing.c	Tue Nov 13 20:50:59 2018 -0500
@@ -33,20 +33,31 @@
 struct str *listing(struct post *post, const char *fname)
 {
 	char path[FILENAME_MAX];
+	uint64_t file_rev;
 	struct str *in;
+	int ret;
 
 	snprintf(path, FILENAME_MAX, "%s/posts/%d/%s",
 		 str_cstr(config.data_dir), post->id, fname);
 
-	in = file_cache_get_cb(path, post->preview ? NULL : revalidate_post,
-			       post);
-	if (IS_ERR(in))
+	in = file_cache_get(path, post->preview ? NULL : revalidate_post,
+			    post, &file_rev);
+	if (IS_ERR(in)) {
+		ret = PTR_ERR(in);
 		goto err;
+	}
+
+	ret = post_add_filename(post, path, file_rev);
+	if (ret)
+		goto err_free;
 
 	return listing_str(in);
 
+err_free:
+	str_putref(in);
+
 err:
 	snprintf(path, FILENAME_MAX, "Failed to read in listing '%d/%s': %s",
-		 post->id, fname, xstrerror(PTR_ERR(in)));
+		 post->id, fname, xstrerror(ret));
 	return STR_DUP(path);
 }
--- a/post.c	Tue Nov 13 16:46:32 2018 -0500
+++ b/post.c	Tue Nov 13 20:50:59 2018 -0500
@@ -81,6 +81,24 @@
 	init_post_index();
 }
 
+int post_add_filename(struct post *post, const char *path, uint64_t cache_rev)
+{
+	return nvl_set_int(post->files, path, cache_rev);
+}
+
+static void post_remove_all_filenames(struct post *post)
+{
+	const struct nvpair *pair;
+
+	while ((pair = nvl_iter_start(post->files)) != NULL) {
+		struct str *name = nvpair_name_str(pair);
+
+		VERIFY0(nvl_unset(post->files, str_cstr(name)));
+
+		str_putref(name);
+	}
+}
+
 void revalidate_post(void *arg)
 {
 	struct post *post = arg;
@@ -138,15 +156,25 @@
 static struct str *load_comment(struct post *post, int commid)
 {
 	char path[FILENAME_MAX];
+	uint64_t file_rev;
 	struct str *out;
 
 	snprintf(path, FILENAME_MAX, "%s/posts/%d/comments/%d/text.txt",
 		 str_cstr(config.data_dir), post->id, commid);
 
-	out = file_cache_get_cb(path, post->preview ? NULL : revalidate_post,
-				post);
-	if (IS_ERR(out))
+	out = file_cache_get(path, post->preview ? NULL : revalidate_post,
+			     post, &file_rev);
+	if (IS_ERR(out)) {
 		out = STATIC_STR("Error: could not load comment text.");
+	} else {
+		int ret;
+
+		ret = post_add_filename(post, path, file_rev);
+		if (ret) {
+			str_putref(out);
+			out = STATIC_STR("Error: internal error has occured.");
+		}
+	}
 
 	return out;
 }
@@ -155,17 +183,22 @@
 {
 	char path[FILENAME_MAX];
 	struct comment *comm;
+	uint64_t file_rev;
 	struct str *meta;
 	struct val *lv;
 	struct val *v;
+	int ret;
 
 	snprintf(path, FILENAME_MAX, "%s/posts/%d/comments/%d/meta.lisp",
 		 str_cstr(config.data_dir), post->id, commid);
 
-	meta = file_cache_get_cb(path, post->preview ? NULL : revalidate_post,
-				 post);
+	meta = file_cache_get(path, post->preview ? NULL : revalidate_post,
+			      post, &file_rev);
 	ASSERT(!IS_ERR(meta));
 
+	ret = post_add_filename(post, path, file_rev);
+	ASSERT0(ret);
+
 	lv = sexpr_parse_str(meta);
 	ASSERT(!IS_ERR(lv));
 
@@ -283,6 +316,7 @@
 	};
 
 	char path[FILENAME_MAX];
+	uint64_t file_rev;
 	struct str *raw;
 	int ret;
 
@@ -291,13 +325,18 @@
 	snprintf(path, FILENAME_MAX, "%s/posts/%d/post.%s",
 		 str_cstr(config.data_dir), post->id, exts[post->fmt]);
 
-	raw = file_cache_get_cb(path, post->preview ? NULL : revalidate_post,
-				post);
+	raw = file_cache_get(path, post->preview ? NULL : revalidate_post,
+			     post, &file_rev);
 	if (IS_ERR(raw))
 		return PTR_ERR(raw);
 
+	ret = post_add_filename(post, path, file_rev);
+	if (ret)
+		goto out;
+
 	ret = __do_load_post_body_fmt3(post, raw);
 
+out:
 	str_putref(raw);
 
 	return ret;
@@ -321,21 +360,27 @@
 static int __refresh_published(struct post *post)
 {
 	char path[FILENAME_MAX];
+	uint64_t file_rev;
 	struct str *meta;
 	struct val *lv;
+	int ret;
 
 	snprintf(path, FILENAME_MAX, "%s/posts/%d/post.lisp",
 		 str_cstr(config.data_dir), post->id);
 
-	meta = file_cache_get_cb(path, post->preview ? NULL : revalidate_post,
-				 post);
+	meta = file_cache_get(path, post->preview ? NULL : revalidate_post,
+			      post, &file_rev);
 	if (IS_ERR(meta))
 		return PTR_ERR(meta);
 
+	ret = post_add_filename(post, path, file_rev);
+	if (ret)
+		goto err;
+
 	lv = sexpr_parse_str(meta);
 	if (IS_ERR(lv)) {
-		str_putref(meta);
-		return PTR_ERR(lv);
+		ret = PTR_ERR(lv);
+		goto err;
 	}
 
 	__refresh_published_prop(post, lv);
@@ -354,12 +399,53 @@
 	str_putref(meta);
 
 	return 0;
+
+err:
+	str_putref(meta);
+
+	return ret;
+
+}
+
+static bool must_refresh(struct post *post)
+{
+	const struct nvpair *pair;
+
+	if (nvl_iter_start(post->files) == NULL)
+		return true; /* no files means we have no idea what is needed */
+
+	nvl_for_each(pair, post->files) {
+		struct str *name = nvpair_name_str(pair);
+		uint64_t file_rev;
+
+		ASSERT0(nvpair_value_int(pair, &file_rev));
+
+		if (!file_cache_has_newer(str_cstr(name), file_rev)) {
+			str_putref(name);
+			continue;
+		}
+
+		cmn_err(CE_DEBUG, "post %u needs a refresh "
+			"('%s' changed, old rev %"PRIu64")", post->id,
+			str_cstr(name), file_rev);
+
+		str_putref(name);
+
+		return true; /* no need to check oher files, we are refreshing */
+	}
+
+	return false;
 }
 
 int __refresh(struct post *post)
 {
 	int ret;
 
+	if (!must_refresh(post))
+		return 0;
+
+	post_remove_all_filenames(post);
+
 	str_putref(post->title);
 	post->title = NULL;
 
@@ -425,6 +511,13 @@
 	refcnt_init(&post->refcnt, 1);
 	MXINIT(&post->lock, &post_lc);
 
+	post->files = nvl_alloc();
+	if (IS_ERR(post->files)) {
+		err = PTR_ERR(post->files);
+		post->files = NULL;
+		goto err_free;
+	}
+
 	if ((err = __refresh(post)))
 		goto err_free;
 
@@ -435,6 +528,7 @@
 
 err_free:
 	post_destroy(post);
+
 err:
 	cmn_err(CE_ERROR, "Failed to load post id %u: %s", postid,
 		xstrerror(err));
@@ -462,6 +556,8 @@
 	post_remove_all_tags(&post->cats);
 	post_remove_all_comments(post);
 
+	nvl_putref(post->files);
+
 	str_putref(post->title);
 	str_putref(post->body);
 
--- a/post.h	Tue Nov 13 16:46:32 2018 -0500
+++ b/post.h	Tue Nov 13 20:50:59 2018 -0500
@@ -77,11 +77,15 @@
 	struct str *body;
 
 	struct str *twitter_img;
+
+	/* filenames used to construct this post */
+	struct nvlist *files;
 };
 
 struct req;
 
 extern void init_post_subsys(void);
+extern int post_add_filename(struct post *post, const char *path, uint64_t cache_rev);
 extern struct post *load_post(int postid, bool preview);
 extern void post_refresh(struct post *post);
 extern void post_destroy(struct post *post);
@@ -106,11 +110,11 @@
 
 REFCNT_INLINE_FXNS(struct post, post, refcnt, post_destroy, NULL)
 
-static inline void post_lock(struct post *post, bool allow_refresh)
+static inline void post_lock(struct post *post, bool refresh)
 {
 	MXLOCK(&post->lock);
 
-	if (allow_refresh && post->needs_refresh)
+	if (refresh)
 		post_refresh(post);
 }