xref: /6.0.3/forestdb/src/transaction.cc (revision 5403f419)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2010 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#include <stdlib.h>
19#include <string.h>
20#include <stdint.h>
21
22#include "libforestdb/forestdb.h"
23#include "fdb_internal.h"
24#include "internal_types.h"
25#include "filemgr.h"
26#include "common.h"
27#include "list.h"
28#include "wal.h"
29#include "memleak.h"
30
31LIBFDB_API
32fdb_status fdb_begin_transaction(fdb_file_handle *fhandle,
33                                 fdb_isolation_level_t isolation_level)
34{
35    if (!fhandle) {
36        return FDB_RESULT_INVALID_ARGS;
37    }
38
39    file_status_t fstatus;
40    fdb_kvs_handle *handle = fhandle->root;
41    struct filemgr *file;
42
43    if (handle->txn) {
44        // transaction already exists
45        return FDB_RESULT_TRANSACTION_FAIL;
46    }
47    if (handle->kvs) {
48        if (handle->kvs->type == KVS_SUB) {
49            // deny transaction on sub handle
50            return FDB_RESULT_INVALID_HANDLE;
51        }
52    }
53
54    if (!atomic_cas_uint8_t(&handle->handle_busy, 0, 1)) {
55        return FDB_RESULT_HANDLE_BUSY;
56    }
57
58    do { // repeat until file status is not REMOVED_PENDING
59        fdb_check_file_reopen(handle, NULL);
60        filemgr_mutex_lock(handle->file);
61        fdb_sync_db_header(handle);
62
63        if (filemgr_is_rollback_on(handle->file)) {
64            // deny beginning transaction during rollback
65            filemgr_mutex_unlock(handle->file);
66            atomic_cas_uint8_t(&handle->handle_busy, 1, 0);
67            return FDB_RESULT_FAIL_BY_ROLLBACK;
68        }
69
70        file = handle->file;
71        fstatus = filemgr_get_file_status(file);
72        if (fstatus == FILE_REMOVED_PENDING) {
73            // we must not create transaction on this file
74            // file status was changed by other thread .. start over
75            filemgr_mutex_unlock(file);
76        }
77    } while (fstatus == FILE_REMOVED_PENDING);
78
79    handle->txn = (fdb_txn*)malloc(sizeof(fdb_txn));
80    handle->txn->wrapper = (struct wal_txn_wrapper *)
81                           malloc(sizeof(struct wal_txn_wrapper));
82    handle->txn->wrapper->txn = handle->txn;
83    handle->txn->handle = handle;
84    if (filemgr_get_file_status(handle->file) != FILE_COMPACT_OLD) {
85        // keep previous header's BID
86        handle->txn->prev_hdr_bid = handle->last_hdr_bid;
87    } else {
88        // if file status is COMPACT_OLD,
89        // then this transaction will work on new file, and
90        // there is no previous header until the compaction is done.
91        handle->txn->prev_hdr_bid = BLK_NOT_FOUND;
92    }
93    handle->txn->prev_revnum = handle->cur_header_revnum;
94    handle->txn->items = (struct list *)malloc(sizeof(struct list));
95    handle->txn->isolation = isolation_level;
96    list_init(handle->txn->items);
97    wal_add_transaction(file, handle->txn);
98
99    filemgr_mutex_unlock(file);
100
101    atomic_cas_uint8_t(&handle->handle_busy, 1, 0);
102    return FDB_RESULT_SUCCESS;
103}
104
105LIBFDB_API
106fdb_status fdb_abort_transaction(fdb_file_handle *fhandle)
107{
108    if (!fhandle) {
109        return FDB_RESULT_INVALID_ARGS;
110    }
111
112    return _fdb_abort_transaction(fhandle->root);
113}
114
115fdb_status _fdb_abort_transaction(fdb_kvs_handle *handle)
116{
117    if (!handle) {
118        return FDB_RESULT_INVALID_ARGS;
119    }
120
121    file_status_t fstatus;
122    struct filemgr *file;
123
124    if (handle->txn == NULL) {
125        // there is no transaction started
126        return FDB_RESULT_TRANSACTION_FAIL;
127    }
128    if (handle->kvs) {
129        if (handle->kvs->type == KVS_SUB) {
130            // deny transaction on sub handle
131            return FDB_RESULT_INVALID_HANDLE;
132        }
133    }
134
135    if (!atomic_cas_uint8_t(&handle->handle_busy, 0, 1)) {
136        return FDB_RESULT_HANDLE_BUSY;
137    }
138
139    do { // repeat until file status is not REMOVED_PENDING
140        fdb_check_file_reopen(handle, NULL);
141
142        file = handle->file;
143        filemgr_mutex_lock(file);
144        fdb_sync_db_header(handle);
145
146        fstatus = filemgr_get_file_status(file);
147        if (fstatus == FILE_REMOVED_PENDING) {
148            // we must not abort transaction on this file
149            // file status was changed by other thread .. start over
150            filemgr_mutex_unlock(file);
151        }
152    } while (fstatus == FILE_REMOVED_PENDING);
153
154    wal_discard(file, handle->txn);
155    wal_remove_transaction(file, handle->txn);
156
157    free(handle->txn->items);
158    free(handle->txn->wrapper);
159    free(handle->txn);
160    handle->txn = NULL;
161
162    filemgr_mutex_unlock(file);
163
164    atomic_cas_uint8_t(&handle->handle_busy, 1, 0);
165    return FDB_RESULT_SUCCESS;
166}
167
168LIBFDB_API
169fdb_status fdb_end_transaction(fdb_file_handle *fhandle,
170                               fdb_commit_opt_t opt)
171{
172    if (!fhandle) {
173        return FDB_RESULT_INVALID_ARGS;
174    }
175
176    file_status_t fstatus;
177    fdb_kvs_handle *handle = fhandle->root;
178    struct filemgr *file;
179
180    if (handle->txn == NULL) {
181        // there is no transaction started
182        return FDB_RESULT_TRANSACTION_FAIL;
183    }
184    if (handle->kvs) {
185        if (handle->kvs->type == KVS_SUB) {
186            // deny transaction on sub handle
187            return FDB_RESULT_INVALID_HANDLE;
188        }
189    }
190
191    if (!atomic_cas_uint8_t(&handle->handle_busy, 0, 1)) {
192        return FDB_RESULT_HANDLE_BUSY;
193    }
194
195    fdb_status fs = FDB_RESULT_SUCCESS;
196    if (list_begin(handle->txn->items)) {
197        fs = _fdb_commit(handle, opt,
198                       !(handle->config.durability_opt & FDB_DRB_ASYNC));
199    }
200
201    if (fs == FDB_RESULT_SUCCESS) {
202
203        do { // repeat until file status is not REMOVED_PENDING
204            fdb_check_file_reopen(handle, NULL);
205
206            file = handle->file;
207            filemgr_mutex_lock(file);
208            fdb_sync_db_header(handle);
209
210            fstatus = filemgr_get_file_status(file);
211            if (fstatus == FILE_REMOVED_PENDING) {
212                // we must not commit transaction on this file
213                // file status was changed by other thread .. start over
214                filemgr_mutex_unlock(file);
215            }
216        } while (fstatus == FILE_REMOVED_PENDING);
217
218        wal_remove_transaction(file, handle->txn);
219
220        free(handle->txn->items);
221        free(handle->txn->wrapper);
222        free(handle->txn);
223        handle->txn = NULL;
224
225        filemgr_mutex_unlock(file);
226    }
227
228    atomic_cas_uint8_t(&handle->handle_busy, 1, 0);
229    return fs;
230}
231