OmniSciDB  72c90bc290
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FileInfo.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2022 HEAVY.AI, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "FileInfo.h"
18 #include <iostream>
19 #include "../../Shared/File.h"
20 #include "FileMgr.h"
21 #include "Page.h"
22 
23 #include <utility>
24 using namespace std;
25 
26 extern bool g_read_only;
27 extern bool g_multi_instance;
28 
29 namespace File_Namespace {
30 
31 FileInfo::FileInfo(FileMgr* fileMgr,
32  const int32_t fileId,
33  FILE* f,
34  const size_t pageSize,
35  size_t numPages,
36  const std::string& file_path,
37  bool init)
38  : fileMgr(fileMgr)
39  , fileId(fileId)
40  , f(f)
41  , pageSize(pageSize)
42  , numPages(numPages)
43  , file_path(file_path) {
44  if (init) {
45  initNewFile();
46  }
47 }
48 
50  // close file, if applicable
51  if (f) {
52  close(f);
53  }
54 }
55 
57  // initialize pages and free page list
58  // Also zeroes out first four bytes of every header
59  for (size_t pageId = 0; pageId < numPages; ++pageId) {
60  freePageImmediate(pageId);
61  }
62 }
63 
64 size_t FileInfo::write(const size_t offset, const size_t size, const int8_t* buf) {
65  std::lock_guard<std::mutex> lock(readWriteMutex_);
66  isDirty = true;
67  return fileMgr->writeFile(f, offset, size, buf);
68 }
69 
70 size_t FileInfo::read(const size_t offset, const size_t size, int8_t* buf) {
71  std::lock_guard<std::mutex> lock(readWriteMutex_);
72  return File_Namespace::read(f, offset, size, buf, file_path);
73 }
74 
75 void FileInfo::openExistingFile(std::vector<HeaderInfo>& headerVec) {
76  // HeaderInfo is defined in Page.h
77 
78  // Oct 2020: Changing semantics such that fileMgrEpoch should be last checkpointed
79  // epoch, not incremented epoch. This changes some of the gt/gte/lt/lte comparison below
80  ChunkKey oldChunkKey(4);
81  int32_t oldPageId = -99;
82  int32_t oldVersionEpoch = -99;
83  int32_t skipped = 0;
84  for (size_t pageNum = 0; pageNum < numPages; ++pageNum) {
85  // TODO(Misiu): It would be nice to replace this array with a struct that would
86  // clarify what is being read and have a single definition (currently this code is
87  // replicated in TableArchiver and possibly elsewhere).
88  constexpr size_t MAX_INTS_TO_READ{10}; // currently use 1+6 ints
89  int32_t ints[MAX_INTS_TO_READ];
90  CHECK_EQ(fseek(f, pageNum * pageSize, SEEK_SET), 0);
91  CHECK_EQ(fread(ints, sizeof(int32_t), MAX_INTS_TO_READ, f), MAX_INTS_TO_READ);
92 
93  auto headerSize = ints[0];
94  if (headerSize == 0) {
95  // no header for this page - insert into free list
96  freePages.insert(pageNum);
97  continue;
98  }
99 
100  // headerSize doesn't include headerSize itself
101  // We're tying ourself to headers of ints here
102  size_t numHeaderElems = headerSize / sizeof(int32_t);
103  CHECK_GE(numHeaderElems, size_t(2));
104  // We don't want to read headerSize in our header - so start
105  // reading 4 bytes past it
106  ChunkKey chunkKey(&ints[1], &ints[1 + numHeaderElems - 2]);
107  if (fileMgr->updatePageIfDeleted(this, chunkKey, ints[1], ints[2], pageNum)) {
108  continue;
109  }
110  // Last two elements of header are always PageId and Version
111  // epoch - these are not in the chunk key so seperate them
112  int32_t pageId = ints[1 + numHeaderElems - 2];
113  int32_t versionEpoch = ints[1 + numHeaderElems - 1];
114  if (chunkKey != oldChunkKey || oldPageId != pageId - (1 + skipped)) {
115  if (skipped > 0) {
116  VLOG(4) << "FId.PSz: " << fileId << "." << pageSize
117  << " Chunk key: " << show_chunk(oldChunkKey)
118  << " Page id from : " << oldPageId << " to : " << oldPageId + skipped
119  << " Epoch: " << oldVersionEpoch;
120  } else if (oldPageId != -99) {
121  VLOG(4) << "FId.PSz: " << fileId << "." << pageSize
122  << " Chunk key: " << show_chunk(oldChunkKey) << " Page id: " << oldPageId
123  << " Epoch: " << oldVersionEpoch;
124  }
125  oldPageId = pageId;
126  oldVersionEpoch = versionEpoch;
127  oldChunkKey = chunkKey;
128  skipped = 0;
129  } else {
130  skipped++;
131  }
132 
133  /* Check if version epoch is equal to
134  * or greater (note: should never be greater)
135  * than FileMgr epoch_ - this means that this
136  * page wasn't checkpointed and thus we should
137  * not use it
138  */
139  int32_t fileMgrEpoch =
140  fileMgr->epoch(chunkKey[CHUNK_KEY_DB_IDX], chunkKey[CHUNK_KEY_TABLE_IDX]);
141  if (versionEpoch > fileMgrEpoch) {
142  // First write 0 to first four bytes of
143  // header to mark as free
144  if (!g_read_only && !g_multi_instance) {
145  // Read-only mode can find pages like this if the server was previously run in
146  // write-mode but is not allowed to free them.
147  freePageImmediate(pageNum);
148  LOG(WARNING) << "Was not checkpointed: Chunk key: " << show_chunk(chunkKey)
149  << " Page id: " << pageId << " Epoch: " << versionEpoch
150  << " FileMgrEpoch " << fileMgrEpoch << endl;
151  }
152  } else { // page was checkpointed properly
153  Page page(fileId, pageNum);
154  headerVec.emplace_back(chunkKey, pageId, versionEpoch, page);
155  }
156  }
157  // printlast
158  if (oldPageId != -99) {
159  if (skipped > 0) {
160  VLOG(4) << "FId.PSz: " << fileId << "." << pageSize
161  << " Chunk key: " << show_chunk(oldChunkKey)
162  << " Page id from : " << oldPageId << " to : " << oldPageId + skipped
163  << " Epoch: " << oldVersionEpoch;
164  } else {
165  VLOG(4) << "FId.PSz: " << fileId << "." << pageSize
166  << " Chunk key: " << show_chunk(oldChunkKey) << " Page id: " << oldPageId
167  << " Epoch: " << oldVersionEpoch;
168  }
169  }
170 }
171 
172 void FileInfo::freePageDeferred(int32_t pageId) {
173  std::lock_guard<std::mutex> lock(freePagesMutex_);
174  freePages.insert(pageId);
175 }
176 
177 #ifdef ENABLE_CRASH_CORRUPTION_TEST
178 #warning "!!!!! DB corruption crash test is enabled !!!!!"
179 #include <signal.h>
180 static bool goto_crash;
181 static void sighandler(int sig) {
182  if (getenv("ENABLE_CRASH_CORRUPTION_TEST"))
183  goto_crash = true;
184 }
185 #endif
186 
187 void FileInfo::freePage(int pageId, const bool isRolloff, int32_t epoch) {
188  int32_t epoch_freed_page[2] = {DELETE_CONTINGENT, epoch};
189  if (isRolloff) {
190  epoch_freed_page[0] = ROLLOFF_CONTINGENT;
191  }
192  write(pageId * pageSize + sizeof(int32_t),
193  sizeof(epoch_freed_page),
194  reinterpret_cast<const int8_t*>(epoch_freed_page));
195  fileMgr->free_page(std::make_pair(this, pageId));
196 
197 #ifdef ENABLE_CRASH_CORRUPTION_TEST
198  signal(SIGUSR2, sighandler);
199  if (goto_crash)
200  CHECK(pageId % 8 != 4);
201 #endif
202 }
203 
205  // returns -1 if there is no free page
206  std::lock_guard<std::mutex> lock(freePagesMutex_);
207  if (freePages.size() == 0) {
208  return -1;
209  }
210  auto pageIt = freePages.begin();
211  int32_t pageNum = *pageIt;
212  freePages.erase(pageIt);
213  return pageNum;
214 }
215 
216 std::string FileInfo::print() const {
217  std::stringstream ss;
218  ss << "File: " << fileId << std::endl;
219  ss << "Size: " << size() << std::endl;
220  ss << "Used: " << used() << std::endl;
221  ss << "Free: " << available() << std::endl;
222  return ss.str();
223 }
224 
226  std::lock_guard<std::mutex> lock(readWriteMutex_);
227  if (isDirty) {
228  if (fflush(f) != 0) {
229  LOG(FATAL) << "Error trying to flush changes to disk, the error was: "
230  << std::strerror(errno);
231  }
232 #ifdef __APPLE__
233  const int32_t sync_result = fcntl(fileno(f), 51);
234 #else
235  const int32_t sync_result = heavyai::fsync(fileno(f));
236 #endif
237  if (sync_result == 0) {
238  isDirty = false;
239  }
240  return sync_result;
241  }
242  return 0; // if file was not dirty and no syncing was needed
243 }
244 
245 void FileInfo::freePageImmediate(int32_t page_num) {
246  int32_t zero{0};
247  write(page_num * pageSize, sizeof(int32_t), reinterpret_cast<const int8_t*>(&zero));
248  freePageDeferred(page_num);
249 }
250 
251 // Overwrites delete/rollback contingents by re-writing chunk key to page.
252 void FileInfo::recoverPage(const ChunkKey& chunk_key, int32_t page_num) {
253  CHECK(!g_multi_instance) << "Attempted unsynchronized write in multi-instance mode";
254  write(page_num * pageSize + sizeof(int32_t),
255  2 * sizeof(int32_t),
256  reinterpret_cast<const int8_t*>(chunk_key.data()));
257 }
258 
259 bool is_page_deleted_with_checkpoint(int32_t table_epoch,
260  int32_t page_epoch,
261  int32_t contingent) {
262  const bool delete_contingent =
263  (contingent == DELETE_CONTINGENT || contingent == ROLLOFF_CONTINGENT);
264  // Check if page was deleted with a checkpointed epoch
265  if (delete_contingent && (table_epoch >= page_epoch)) {
266  return true;
267  }
268  return false;
269 }
270 
271 bool is_page_deleted_without_checkpoint(int32_t table_epoch,
272  int32_t page_epoch,
273  int32_t contingent) {
274  const bool delete_contingent =
275  (contingent == DELETE_CONTINGENT || contingent == ROLLOFF_CONTINGENT);
276  // Check if page was deleted but the epoch was not yet checkpointed.
277  if (delete_contingent && (table_epoch < page_epoch)) {
278  return true;
279  }
280  return false;
281 }
282 } // namespace File_Namespace
virtual int32_t epoch(int32_t db_id, int32_t tb_id) const
Returns current value of epoch - should be one greater than recorded at last checkpoint. Because FileMgr only contains buffers from one table we can just return the FileMgr&#39;s epoch instead of finding a table-specific epoch.
Definition: FileMgr.h:281
#define CHECK_EQ(x, y)
Definition: Logger.h:301
std::vector< int > ChunkKey
Definition: types.h:36
size_t write(const size_t offset, const size_t size, const int8_t *buf)
Definition: FileInfo.cpp:64
bool g_multi_instance
Definition: heavyai_locks.h:22
bool is_page_deleted_without_checkpoint(int32_t table_epoch, int32_t page_epoch, int32_t contingent)
Definition: FileInfo.cpp:271
A logical page (Page) belongs to a file on disk.
Definition: Page.h:46
#define LOG(tag)
Definition: Logger.h:285
std::mutex readWriteMutex_
Definition: FileInfo.h:65
#define CHUNK_KEY_DB_IDX
Definition: types.h:38
void freePageImmediate(int32_t page_num)
Definition: FileInfo.cpp:245
This file includes the class specification for the FILE manager (FileMgr), and related data structure...
#define CHECK_GE(x, y)
Definition: Logger.h:306
std::string print() const
Prints a summary of the file to stdout.
Definition: FileInfo.cpp:216
virtual bool updatePageIfDeleted(FileInfo *file_info, ChunkKey &chunk_key, int32_t contingent, int32_t page_epoch, int32_t page_num)
deletes or recovers a page based on last checkpointed epoch.
Definition: FileMgr.cpp:1637
std::string file_path
set of page numbers of free pages
Definition: FileInfo.h:63
std::string show_chunk(const ChunkKey &key)
Definition: types.h:98
void freePage(int32_t pageId, const bool isRolloff, int32_t epoch)
Definition: FileInfo.cpp:187
std::set< size_t > freePages
Definition: FileInfo.h:62
size_t pageSize
file stream object for the represented file
Definition: FileInfo.h:59
constexpr int32_t DELETE_CONTINGENT
A FileInfo type has a file pointer and metadata about a file.
Definition: FileInfo.h:51
void init(LogOptions const &log_opts)
Definition: Logger.cpp:364
size_t used() const
Returns the amount of used bytes; size() - available()
Definition: FileInfo.h:116
void initNewFile()
Adds all pages to freePages and zeroes first four bytes of header.
Definition: FileInfo.cpp:56
size_t size() const
Returns the number of bytes used by the file.
Definition: FileInfo.h:95
#define CHUNK_KEY_TABLE_IDX
Definition: types.h:39
std::mutex freePagesMutex_
Definition: FileInfo.h:64
size_t read(FILE *f, const size_t offset, const size_t size, int8_t *buf, const std::string &file_path)
Reads the specified number of bytes from the offset position in file f into buf.
Definition: File.cpp:125
constexpr int32_t ROLLOFF_CONTINGENT
Definition: FileInfo.h:52
bool g_read_only
Definition: heavyai_locks.h:21
~FileInfo()
Destructor.
Definition: FileInfo.cpp:49
size_t read(const size_t offset, const size_t size, int8_t *buf)
Definition: FileInfo.cpp:70
bool is_page_deleted_with_checkpoint(int32_t table_epoch, int32_t page_epoch, int32_t contingent)
Definition: FileInfo.cpp:259
torch::Tensor f(torch::Tensor x, torch::Tensor W_target, torch::Tensor b_target)
virtual void free_page(std::pair< FileInfo *, int32_t > &&page)
Definition: FileMgr.cpp:1210
FILE * f
unique file identifier (i.e., used for a file name)
Definition: FileInfo.h:58
int fsync(int fd)
Definition: heavyai_fs.cpp:62
This file contains the declaration and definition of a Page type and a MultiPage type.
void openExistingFile(std::vector< HeaderInfo > &headerVec)
Definition: FileInfo.cpp:75
#define CHECK(condition)
Definition: Logger.h:291
void recoverPage(const ChunkKey &chunk_key, int32_t page_num)
Definition: FileInfo.cpp:252
void close(FILE *f)
Closes the file pointed to by the FILE pointer.
Definition: File.cpp:114
void freePageDeferred(int32_t pageId)
Definition: FileInfo.cpp:172
size_t available() const
Returns the number of free bytes available.
Definition: FileInfo.h:102
size_t writeFile(FILE *f, const size_t offset, const size_t size, const int8_t *buf) const
Definition: FileMgr.cpp:1715
size_t numPages
the fixed size of each page in the file
Definition: FileInfo.h:60
bool isDirty
the number of pages in the file
Definition: FileInfo.h:61
#define VLOG(n)
Definition: Logger.h:388