b13983ea68c8185bf8678d91d9fb665e101652ba
[bluesky.git] / bluesky / cleaner.c
1 /* Blue Sky: File Systems in the Cloud
2  *
3  * Copyright (C) 2010  The Regents of the University of California
4  * Written by Michael Vrable <mvrable@cs.ucsd.edu>
5  *
6  * TODO: Licensing
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdint.h>
12 #include <inttypes.h>
13 #include <glib.h>
14 #include <string.h>
15
16 #include "bluesky-private.h"
17
18 /* Proxy component of the file system cleaner.  This consists effectively of
19  * code for merging multiple versions of the file system logs: that generated
20  * by us and that generated by the in-cloud cleaner.  Other logic, such as for
21  * rewriting log segments where needed and deleting old segments, is handled by
22  * the in-cloud cleaner component. */
23
24 /* A specialized function for loading an object from the cloud, used just by
25  * the cleaner.  When the cleaner runs, it will produce new objects that have
26  * the same object ID as before, differing only in the outgoing link locations
27  * as a result of cleaning.  We can't use the normal functions for loading
28  * objects since those can only deal with one version of an object.  Instead,
29  * we load the cleaned object with this function, and then merge the state into
30  * the official object as needed.
31  *
32  * The bluesky_cleaner_deserialize function is like
33  * bluesky_deserialize_cloudlog, but does a more limited deserialization and
34  * keeps the returned item entirely separate from any in-memory BlueSkyCloudLog
35  * objects. */
36
37 typedef struct {
38     BlueSkyCloudLogType type;
39     BlueSkyCloudID id;
40     BlueSkyCloudPointer location;
41     uint64_t inum;
42     GArray *links;
43 } BlueSkyCleanerItem;
44
45 typedef struct {
46     BlueSkyCloudID id;
47     BlueSkyCloudPointer location;
48 } BlueSkyCleanerLink;
49
50 static BlueSkyCleanerItem *bluesky_cleaner_deserialize(BlueSkyRCStr *raw)
51 {
52     const char *data = raw->data;
53     size_t len = raw->len;
54     const char *data1, *data2, *data3;
55     size_t len1, len2, len3;
56
57     g_assert(len > 4);
58     if (len < sizeof(struct cloudlog_header)
59         || memcmp(data, CLOUDLOG_MAGIC, 4) != 0)
60     {
61         g_warning("Deserializing garbage cloud log item from cleaner!");
62         return NULL;
63     };
64
65     struct cloudlog_header *header = (struct cloudlog_header *)data;
66     len1 = GUINT32_FROM_LE(header->size1);
67     len2 = GUINT32_FROM_LE(header->size2);
68     len3 = GUINT32_FROM_LE(header->size3);
69     data1 = data + sizeof(struct cloudlog_header);
70     data2 = data1 + len1;
71     data3 = data2 + len2;
72     g_assert(data3 + len3 - data <= len);
73
74     BlueSkyCleanerItem *item = g_new0(BlueSkyCleanerItem, 1);
75     item->type = header->type - '0';
76     item->inum = GUINT64_FROM_LE(header->inum);
77     memcpy(&item->id, &header->id, sizeof(BlueSkyCloudID));
78
79     int link_count = len2 / sizeof(BlueSkyCloudID);
80     g_print("Outgoing links: %d\n", link_count);
81     item->links = g_array_new(FALSE, TRUE, sizeof(BlueSkyCleanerLink));
82     for (int i = 0; i < link_count; i++) {
83         BlueSkyCleanerLink link;
84
85         g_assert(len2 >= sizeof(link.id));
86         memcpy(&link.id, data2, sizeof(link.id));
87         data2 += sizeof(link.id); len2 -= sizeof(link.id);
88
89         g_assert(len3 >= sizeof(link.location));
90         memcpy(&link.location, data3, sizeof(link.location));
91         data3 += sizeof(link.location); len3 -= sizeof(link.location);
92
93         g_array_append_val(item->links, link);
94     }
95
96     return item;
97 }
98
99 static void bluesky_cleaner_item_free(BlueSkyCleanerItem *item)
100 {
101     if (item == NULL)
102         return;
103     g_array_unref(item->links);
104     g_free(item);
105 }
106
107 /* Check the cleaner's logs to find the a more recent checkpoint record.  This
108  * should be called occasionally to see if the cleaner has done any work since
109  * our last check. */
110 static BlueSkyCleanerItem *bluesky_cleaner_find_checkpoint(BlueSkyFS *fs)
111 {
112     char *prefix = g_strdup_printf("log-%08d", BLUESKY_CLOUD_DIR_CLEANER);
113     char *last_segment = bluesky_store_lookup_last(fs->store, prefix);
114     g_free(prefix);
115     if (last_segment == NULL)
116         return NULL;
117
118     g_print("Last cloud log segment: %s\n", last_segment);
119     int seq = atoi(last_segment + 13);
120     g_free(last_segment);
121
122     if (seq <= fs->log_state->latest_cleaner_seq_seen)
123         return NULL;
124
125     g_print("New log segment appeared in cleaner directory: %d\n", seq);
126
127     BlueSkyCacheFile *cachefile;
128     cachefile = bluesky_cachefile_lookup(fs, BLUESKY_CLOUD_DIR_CLEANER, seq,
129                                          TRUE);
130     while (!cachefile->complete)
131         g_cond_wait(cachefile->cond, cachefile->lock);
132
133     g_print("Downloaded latest cleaner segment.\n");
134
135     int64_t offset = -1, length = 0;
136     while (TRUE) {
137         const BlueSkyRangesetItem *item;
138         item = bluesky_rangeset_lookup_next(cachefile->items, offset + 1);
139         if (item == NULL)
140             break;
141         offset = item->start;
142         length = item->length;
143     }
144
145     if (length == 0)
146         return NULL;
147
148     g_print("Found a cleaner checkpoint record.\n");
149
150     BlueSkyRCStr *data = bluesky_cachefile_map_raw(cachefile, offset, length);
151     bluesky_cachefile_unref(cachefile);
152     g_mutex_unlock(cachefile->lock);
153
154     BlueSkyCleanerItem *checkpoint = bluesky_cleaner_deserialize(data);
155     bluesky_string_unref(data);
156
157     return checkpoint;
158 }
159
160 static BlueSkyCleanerItem *cleaner_load_item(BlueSkyFS *fs,
161                                              BlueSkyCloudPointer location)
162 {
163     g_print("Loading item %d/%d/%d...\n", location.directory, location.sequence, location.offset);
164
165     BlueSkyCacheFile *cachefile;
166     cachefile = bluesky_cachefile_lookup(fs, location.directory,
167                                          location.sequence, TRUE);
168     while (!cachefile->complete)
169         g_cond_wait(cachefile->cond, cachefile->lock);
170
171     /* TODO: Ought to check that we are loading an item which validated? */
172     BlueSkyRCStr *data = bluesky_cachefile_map_raw(cachefile, location.offset,
173                                                    location.size);
174     bluesky_cachefile_unref(cachefile);
175     g_mutex_unlock(cachefile->lock);
176
177     BlueSkyCleanerItem *item = bluesky_cleaner_deserialize(data);
178     bluesky_string_unref(data);
179
180     return item;
181 }
182
183 /* Does the item at the given cloud location from the cleaner need merging?  An
184  * item in the primary log does not need to be merged, as by definition we
185  * already know about it.  Similarly, old items in the cleaner's log--those
186  * that we have already seen from a previous merge--do not need to be mergd
187  * again. */
188 gboolean needs_merging(BlueSkyFS *fs, BlueSkyCloudPointer location)
189 {
190     if (location.directory == BLUESKY_CLOUD_DIR_PRIMARY)
191         return FALSE;
192
193     return TRUE;
194 }
195
196 void bluesky_cleaner_merge(BlueSkyFS *fs)
197 {
198     BlueSkyCleanerItem *checkpoint = bluesky_cleaner_find_checkpoint(fs);
199
200     if (checkpoint == NULL) {
201         g_warning("Unable to load cleaner checkpoint record!");
202         return;
203     }
204
205     /* Iterate over each of the inode map sections in the checkpoint */
206     for (int i = 0; i < checkpoint->links->len; i++) {
207         BlueSkyCleanerLink *link = &g_array_index(checkpoint->links,
208                                                   BlueSkyCleanerLink, i);
209         /*if (!needs_merging(fs, link->location))
210             continue; */
211
212         BlueSkyCleanerItem *imap = cleaner_load_item(fs, link->location);
213         if (imap == NULL) {
214             g_warning("Unable to load cleaner inode map");
215             continue;
216         }
217
218         /* Iterate over all inodes found in the inode map section */
219         for (int j = 0; j < imap->links->len; j++) {
220             BlueSkyCleanerLink *link = &g_array_index(imap->links,
221                                                       BlueSkyCleanerLink, j);
222             BlueSkyCleanerItem *inode = cleaner_load_item(fs, link->location);
223             if (inode != NULL) {
224                 g_print("Got inode %"PRIu64"\n", inode->inum);
225             }
226             bluesky_cleaner_item_free(inode);
227         }
228
229         bluesky_cleaner_item_free(imap);
230     }
231
232     bluesky_cleaner_item_free(checkpoint);
233 }