1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
3/**
4 * @copyright 2014 Couchbase, Inc.
5 *
6 * @author Sarath Lakshman  <sarath@couchbase.com>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 * use this file except in compliance with the License. You may obtain a copy of
10 * the License at
11 *
12 *  http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 * License for the specific language governing permissions and limitations under
18 * the License.
19 **/
20
21#include "config.h"
22
23#include <platform/cb_malloc.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include "../view_group.h"
28#include "../util.h"
29#include "util.h"
30#include "../mapreduce/mapreduce.h"
31
32#define BUF_SIZE 8192
33#define MAX(a,b) ((a) > (b) ? a : b)
34
35static void stats_updater(uint64_t freq, uint64_t inserted)
36{
37    if (inserted % freq == 0) {
38        fprintf(stdout, "Stats = inserted : %" PRIu64 "\n", freq);
39    }
40}
41
42int main(int argc, char *argv[])
43{
44    view_group_info_t *group_info = NULL;
45    char buf[BUF_SIZE];
46    char *target_file = NULL;
47    couchstore_error_t ret = COUCHSTORE_SUCCESS;
48    sized_buf header_buf = {NULL, 0};
49    sized_buf header_outbuf = {NULL, 0};
50    uint64_t total_changes = 0;
51    uint64_t header_size = 0;
52    view_error_t error_info = {NULL, NULL, "GENERIC"};
53    cb_thread_t exit_thread;
54    compactor_stats_t stats;
55
56    (void) argc;
57    (void) argv;
58
59    /*
60     * Disable buffering for stdout/stderr since progress stats needs to be
61     * immediately available at erlang side
62     */
63    setvbuf(stdout, (char *) NULL, _IONBF, 0);
64    setvbuf(stderr, (char *) NULL, _IONBF, 0);
65
66    if (set_binary_mode() < 0) {
67        fprintf(stderr, "Error setting binary mode\n");
68        goto out;
69    }
70
71    /* Read target filepath */
72    if (couchstore_read_line(stdin, buf, BUF_SIZE) != buf) {
73        fprintf(stderr, "Error reading compaction target filepath\n");
74        ret = COUCHSTORE_ERROR_INVALID_ARGUMENTS;
75        goto out;
76    }
77
78    target_file = cb_strdup(buf);
79    if (target_file == NULL) {
80        fprintf(stderr, "Memory allocation failure\n");
81        ret = COUCHSTORE_ERROR_ALLOC_FAIL;
82        goto out;
83    }
84
85    total_changes = couchstore_read_int(stdin, buf, sizeof(buf), &ret);
86    if (ret != COUCHSTORE_SUCCESS) {
87        fprintf(stderr, "Error reading total changes\n");
88        ret = COUCHSTORE_ERROR_INVALID_ARGUMENTS;
89        goto out;
90    }
91
92    group_info = couchstore_read_view_group_info(stdin, stderr);
93    if (group_info == NULL) {
94        ret = COUCHSTORE_ERROR_ALLOC_FAIL;
95        goto out;
96    }
97
98    /* Read group header bin */
99    header_size = couchstore_read_int(stdin, buf, sizeof(buf), &ret);
100    if (ret != COUCHSTORE_SUCCESS) {
101        fprintf(stderr, "Error reading viewgroup header size\n");
102        ret = COUCHSTORE_ERROR_INVALID_ARGUMENTS;
103        goto out;
104    }
105
106    if (header_size > MAX_VIEW_HEADER_SIZE) {
107        fprintf(stderr, "View header is too large (%" PRIu64 " bytes). "
108                "Maximum size is %d bytes\n",
109                header_size, MAX_VIEW_HEADER_SIZE);
110        ret = COUCHSTORE_ERROR_INVALID_ARGUMENTS;
111        goto out;
112    }
113
114    header_buf.size = (size_t)header_size;
115    header_buf.buf = (char*)cb_malloc(header_buf.size);
116    if (header_buf.buf == NULL) {
117        fprintf(stderr, "Memory allocation failure\n");
118        ret = COUCHSTORE_ERROR_ALLOC_FAIL;
119        goto out;
120    }
121
122    if (fread(header_buf.buf, header_buf.size, 1, stdin) != 1) {
123        fprintf(stderr,
124                "Error reading viewgroup header from stdin\n");
125        ret = COUCHSTORE_ERROR_INVALID_ARGUMENTS;
126        goto out;
127    }
128
129    /* Setup stats update frequency to be percentage increment */
130    stats.inserted = 0;
131    stats.update_fun = stats_updater;
132    stats.freq = MAX(total_changes / 100, 1);
133
134    ret = (couchstore_error_t)start_exit_listener(&exit_thread, 1 /*uses_v8*/);
135    if (ret) {
136        fprintf(stderr, "Error starting stdin exit listener thread\n");
137        goto out;
138    }
139
140    mapreduce_init();
141    ret = couchstore_compact_view_group(group_info,
142                                        target_file,
143                                        &header_buf,
144                                        &stats,
145                                        &header_outbuf,
146                                        &error_info);
147    mapreduce_deinit();
148
149    if (ret != COUCHSTORE_SUCCESS) {
150        if (error_info.error_msg != NULL && error_info.view_name != NULL) {
151            fprintf(stderr,
152                    "%s Error compacting index for view `%s`, reason: %s\n",
153                    error_info.idx_type,
154                    error_info.view_name,
155                    error_info.error_msg);
156        }
157        goto out;
158    }
159
160    fprintf(stdout, "Header Len : %lu\n", header_outbuf.size);
161    fwrite(header_outbuf.buf, header_outbuf.size, 1, stdout);
162    fprintf(stdout, "\n");
163
164    fprintf(stdout, "Results = inserts : %" PRIu64 "\n", stats.inserted);
165
166out:
167    couchstore_free_view_group_info(group_info);
168    cb_free((void *) error_info.error_msg);
169    cb_free((void *) error_info.view_name);
170    cb_free((void *) header_buf.buf);
171    cb_free((void *) header_outbuf.buf);
172    cb_free(target_file);
173
174    int ret_int = (int)ret;
175    ret_int = (ret_int < 0) ? (100 + ret_int) : ret_int;
176    _exit(ret_int);
177}
178