Line data Source code
1 : /**
2 : * Copyright Soramitsu Co., Ltd. All Rights Reserved.
3 : * SPDX-License-Identifier: Apache-2.0
4 : */
5 :
6 : #include "ametsuchi/impl/flat_file/flat_file.hpp"
7 :
8 : #include <boost/filesystem.hpp>
9 : #include <boost/range/adaptor/indexed.hpp>
10 : #include <boost/range/algorithm/find_if.hpp>
11 : #include <iomanip>
12 : #include <iostream>
13 : #include <sstream>
14 : #include "common/files.hpp"
15 :
16 : using namespace iroha::ametsuchi;
17 : using Identifier = FlatFile::Identifier;
18 :
19 : // ----------| public API |----------
20 :
21 : std::string FlatFile::id_to_name(Identifier id) {
22 3169 : std::ostringstream os;
23 3170 : os << std::setw(FlatFile::DIGIT_CAPACITY) << std::setfill('0') << id;
24 3170 : return os.str();
25 3170 : }
26 :
27 : boost::optional<std::unique_ptr<FlatFile>> FlatFile::create(
28 : const std::string &path) {
29 480 : auto log_ = logger::log("FlatFile::create()");
30 :
31 480 : boost::system::error_code err;
32 480 : if (not boost::filesystem::is_directory(path, err)
33 480 : and not boost::filesystem::create_directory(path, err)) {
34 1 : log_->error("Cannot create storage dir: {}\n{}", path, err.message());
35 1 : return boost::none;
36 : }
37 :
38 479 : auto res = FlatFile::check_consistency(path);
39 479 : return std::make_unique<FlatFile>(*res, path, private_tag{});
40 480 : }
41 :
42 : bool FlatFile::add(Identifier id, const Bytes &block) {
43 : // TODO(x3medima17): Change bool to generic Result return type
44 :
45 1297 : if (id != current_id_ + 1) {
46 4 : log_->warn("Cannot append non-consecutive block");
47 4 : return false;
48 : }
49 :
50 1293 : auto next_id = id;
51 1293 : const auto file_name = boost::filesystem::path{dump_dir_} / id_to_name(id);
52 :
53 : // Write block to binary file
54 1293 : if (boost::filesystem::exists(file_name)) {
55 : // File already exist
56 1 : log_->warn("insertion for {} failed, because file already exists", id);
57 1 : return false;
58 : }
59 : // New file will be created
60 1292 : boost::filesystem::ofstream file(file_name.native(), std::ofstream::binary);
61 1292 : if (not file.is_open()) {
62 1 : log_->warn("Cannot open file by index {} for writing", id);
63 1 : return false;
64 : }
65 :
66 1291 : auto val_size =
67 : sizeof(std::remove_reference<decltype(block)>::type::value_type);
68 :
69 1291 : file.write(reinterpret_cast<const char *>(block.data()),
70 1291 : block.size() * val_size);
71 :
72 : // Update internals, release lock
73 1291 : current_id_ = next_id;
74 1291 : return true;
75 1297 : }
76 :
77 : boost::optional<FlatFile::Bytes> FlatFile::get(Identifier id) const {
78 : const auto filename =
79 1864 : boost::filesystem::path{dump_dir_} / FlatFile::id_to_name(id);
80 1864 : if (not boost::filesystem::exists(filename)) {
81 520 : log_->info("get({}) file not found", id);
82 : return boost::none;
83 : }
84 1344 : const auto fileSize = boost::filesystem::file_size(filename);
85 1344 : Bytes buf;
86 1344 : buf.resize(fileSize);
87 1343 : boost::filesystem::ifstream file(filename, std::ifstream::binary);
88 1345 : if (not file.is_open()) {
89 0 : log_->info("get({}) problem with opening file", id);
90 0 : return boost::none;
91 : }
92 : file.read(reinterpret_cast<char *>(buf.data()), fileSize);
93 1345 : return buf;
94 1865 : }
95 :
96 : std::string FlatFile::directory() const {
97 1 : return dump_dir_;
98 : }
99 :
100 : Identifier FlatFile::last_id() const {
101 2710 : return current_id_.load();
102 : }
103 :
104 : void FlatFile::dropAll() {
105 922 : iroha::remove_dir_contents(dump_dir_);
106 922 : auto res = FlatFile::check_consistency(dump_dir_);
107 922 : current_id_.store(*res);
108 922 : }
109 :
110 : // ----------| private API |----------
111 :
112 : FlatFile::FlatFile(Identifier current_id,
113 : const std::string &path,
114 : FlatFile::private_tag)
115 479 : : dump_dir_(path) {
116 479 : log_ = logger::log("FlatFile");
117 479 : current_id_.store(current_id);
118 479 : }
119 :
120 : boost::optional<Identifier> FlatFile::check_consistency(
121 : const std::string &dump_dir) {
122 1402 : auto log = logger::log("FLAT_FILE");
123 :
124 1402 : if (dump_dir.empty()) {
125 1 : log->error("check_consistency({}), not directory", dump_dir);
126 1 : return boost::none;
127 : }
128 :
129 : auto const files = [&dump_dir] {
130 1401 : std::vector<boost::filesystem::path> ps;
131 1401 : std::copy(boost::filesystem::directory_iterator{dump_dir},
132 1401 : boost::filesystem::directory_iterator{},
133 1401 : std::back_inserter(ps));
134 1401 : std::sort(ps.begin(), ps.end(), std::less<boost::filesystem::path>());
135 1401 : return ps;
136 1401 : }();
137 :
138 1401 : auto const missing = boost::range::find_if(
139 : files | boost::adaptors::indexed(1), [](const auto &it) {
140 8 : return FlatFile::id_to_name(it.index()) != it.value().filename();
141 0 : });
142 :
143 1401 : std::for_each(
144 : missing.get(), files.cend(), [](const boost::filesystem::path &p) {
145 1 : boost::filesystem::remove(p);
146 1 : });
147 :
148 1401 : return missing.get() - files.cbegin();
149 1402 : }
|