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