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#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include "../view_group.h"
26#include "../util.h"
27#include "util.h"
28
29#define BUF_SIZE 8192
30#define MAX(a,b) ((a) > (b) ? a : b)
31
32static void stats_updater(uint64_t freq, uint64_t inserted)
33{
34    if (inserted % freq == 0) {
35        fprintf(stdout, "Stats = inserted : %"PRIu64"\n", freq);
36    }
37}
38
39int main(int argc, char *argv[])
40{
41    view_group_info_t *group_info = NULL;
42    char buf[BUF_SIZE];
43    char *target_file = NULL;
44    int ret = COUCHSTORE_SUCCESS;
45    sized_buf header_buf = {NULL, 0};
46    sized_buf header_outbuf = {NULL, 0};
47    uint64_t total_changes = 0;
48    view_error_t error_info = {NULL, NULL, "GENERIC"};
49    cb_thread_t exit_thread;
50    compactor_stats_t stats;
51
52    (void) argc;
53    (void) argv;
54
55    /*
56     * Disable buffering for stdout since progress stats needs to be
57     * immediately available at erlang side
58     */
59    setvbuf(stdout, (char *) NULL, _IONBF, 0);
60
61    if (set_binary_mode() < 0) {
62        fprintf(stderr, "Error setting binary mode\n");
63        goto out;
64    }
65
66    /* Read target filepath */
67    if (couchstore_read_line(stdin, buf, BUF_SIZE) != buf) {
68        fprintf(stderr, "Error reading compaction target filepath\n");
69        ret = COUCHSTORE_ERROR_INVALID_ARGUMENTS;
70        goto out;
71    }
72
73    target_file = strdup(buf);
74    if (target_file == NULL) {
75        fprintf(stderr, "Memory allocation failure\n");
76        ret = COUCHSTORE_ERROR_ALLOC_FAIL;
77        goto out;
78    }
79
80    total_changes = couchstore_read_int(stdin, buf, sizeof(buf), &ret);
81    if (ret != COUCHSTORE_SUCCESS) {
82        fprintf(stderr, "Error reading total changes\n");
83        ret = COUCHSTORE_ERROR_INVALID_ARGUMENTS;
84        goto out;
85    }
86
87    group_info = couchstore_read_view_group_info(stdin, stderr);
88    if (group_info == NULL) {
89        ret = COUCHSTORE_ERROR_ALLOC_FAIL;
90        goto out;
91    }
92
93    /* Read group header bin */
94    header_buf.size = couchstore_read_int(stdin, buf, sizeof(buf), &ret);
95    if (ret != COUCHSTORE_SUCCESS) {
96        fprintf(stderr, "Error reading viewgroup header size\n");
97        ret = COUCHSTORE_ERROR_INVALID_ARGUMENTS;
98        goto out;
99    }
100
101    header_buf.buf = malloc(header_buf.size);
102    if (header_buf.buf == NULL) {
103        fprintf(stderr, "Memory allocation failure\n");
104        ret = COUCHSTORE_ERROR_ALLOC_FAIL;
105        goto out;
106    }
107
108    if (fread(header_buf.buf, header_buf.size, 1, stdin) != 1) {
109        fprintf(stderr,
110                "Error reading viewgroup header from stdin\n");
111        ret = COUCHSTORE_ERROR_INVALID_ARGUMENTS;
112        goto out;
113    }
114
115    /* Setup stats update frequency to be percentage increment */
116    stats.inserted = 0;
117    stats.update_fun = stats_updater;
118    stats.freq = MAX(total_changes / 100, 1);
119
120    ret = start_exit_listener(&exit_thread);
121    if (ret) {
122        fprintf(stderr, "Error starting stdin exit listener thread\n");
123        goto out;
124    }
125
126    ret = couchstore_compact_view_group(group_info,
127                                        target_file,
128                                        &header_buf,
129                                        &stats,
130                                        &header_outbuf,
131                                        &error_info);
132
133    if (ret != COUCHSTORE_SUCCESS) {
134        if (error_info.error_msg != NULL && error_info.view_name != NULL) {
135            fprintf(stderr,
136                    "%s Error compacting index for view `%s`, reason: %s\n",
137                    error_info.idx_type,
138                    error_info.view_name,
139                    error_info.error_msg);
140        }
141        goto out;
142    }
143
144    fprintf(stdout, "Header Len : %lu\n", header_outbuf.size);
145    fwrite(header_outbuf.buf, header_outbuf.size, 1, stdout);
146    fprintf(stdout, "\n");
147
148    fprintf(stdout, "Results = inserts : %"PRIu64"\n", stats.inserted);
149
150out:
151    couchstore_free_view_group_info(group_info);
152    free((void *) error_info.error_msg);
153    free((void *) error_info.view_name);
154    free((void *) header_buf.buf);
155    free((void *) header_outbuf.buf);
156    free(target_file);
157
158    return (ret < 0) ? (100 + ret) : ret;
159}
160
161