1 // mmap_v1_extent_manager.h
2 
3 
4 /**
5  *    Copyright (C) 2018-present MongoDB, Inc.
6  *
7  *    This program is free software: you can redistribute it and/or modify
8  *    it under the terms of the Server Side Public License, version 1,
9  *    as published by MongoDB, Inc.
10  *
11  *    This program is distributed in the hope that it will be useful,
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *    Server Side Public License for more details.
15  *
16  *    You should have received a copy of the Server Side Public License
17  *    along with this program. If not, see
18  *    <http://www.mongodb.com/licensing/server-side-public-license>.
19  *
20  *    As a special exception, the copyright holders give permission to link the
21  *    code of portions of this program with the OpenSSL library under certain
22  *    conditions as described in each individual source file and distribute
23  *    linked combinations including the program with the OpenSSL library. You
24  *    must comply with the Server Side Public License in all respects for
25  *    all of the code used other than as permitted herein. If you modify file(s)
26  *    with this exception, you may extend this exception to your version of the
27  *    file(s), but you are not obligated to do so. If you do not wish to do so,
28  *    delete this exception statement from your version. If you delete this
29  *    exception statement from all source files in the program, then also delete
30  *    it in the license file.
31  */
32 
33 #pragma once
34 
35 #include <string>
36 
37 #include <boost/filesystem/path.hpp>
38 
39 #include "mongo/base/status.h"
40 #include "mongo/base/string_data.h"
41 #include "mongo/db/concurrency/lock_manager_defs.h"
42 #include "mongo/db/storage/mmap_v1/diskloc.h"
43 #include "mongo/db/storage/mmap_v1/extent_manager.h"
44 #include "mongo/db/storage/mmap_v1/record_access_tracker.h"
45 #include "mongo/platform/atomic_word.h"
46 #include "mongo/stdx/mutex.h"
47 
48 namespace mongo {
49 
50 class DataFile;
51 class DataFileVersion;
52 class MmapV1RecordHeader;
53 class OperationContext;
54 
55 struct Extent;
56 
57 /**
58  * ExtentManager basics
59  *  - one per database
60  *  - responsible for managing <db>.# files
61  *  - NOT responsible for .ns file
62  *  - gives out extents
63  *  - responsible for figuring out how to get a new extent
64  *  - can use any method it wants to do so
65  *  - this structure is NOT stored on disk
66  *  - this class is thread safe, except as indicated below
67  *
68  * Implementation:
69  *  - ExtentManager holds a preallocated list of DataFile
70  *  - files will not be removed from the EM, so _files access can be lock-free
71  *  - extent size and loc are immutable
72  *  - Any non-const public operations on an ExtentManager will acquire an MODE_X lock on its
73  *    RESOURCE_MMAPv1_EXTENT_MANAGER resource from the lock-manager, which will extend life
74  *    to during WriteUnitOfWorks that might need rollback. Private methods will only
75  *    be called from public ones.
76  */
77 class MmapV1ExtentManager : public ExtentManager {
78     MONGO_DISALLOW_COPYING(MmapV1ExtentManager);
79 
80 public:
81     class Factory : public ExtentManager::Factory {
82         virtual std::unique_ptr<ExtentManager> create(StringData dbname,
83                                                       StringData path,
84                                                       bool directoryPerDB) final;
85     };
86 
87     /**
88      * @param freeListDetails this is a reference into the .ns file
89      *        while a bit odd, this is not a layer violation as extents
90      *        are a peer to the .ns file, without any layering
91      */
92     MmapV1ExtentManager(StringData dbname, StringData path, bool directoryPerDB);
93 
94     /**
95      * Must be called before destruction.
96      */
close(OperationContext * opCtx)97     void close(OperationContext* opCtx) {
98         _files.close(opCtx);
99     }
100 
101     /**
102      * opens all current files, not thread safe
103      */
104     Status init(OperationContext* opCtx);
105 
106     int numFiles() const;
107     long long fileSize() const;
108 
109     // must call Extent::reuse on the returned extent
110     DiskLoc allocateExtent(OperationContext* opCtx, bool capped, int size, bool enforceQuota);
111 
112     /**
113      * firstExt has to be == lastExt or a chain
114      */
115     void freeExtents(OperationContext* opCtx, DiskLoc firstExt, DiskLoc lastExt);
116 
117     /**
118      * frees a single extent
119      * ignores all fields in the Extent except: magic, myLoc, length
120      */
121     void freeExtent(OperationContext* opCtx, DiskLoc extent);
122 
123 
124     void freeListStats(OperationContext* opCtx, int* numExtents, int64_t* totalFreeSizeBytes) const;
125 
126     /**
127      * @param loc - has to be for a specific MmapV1RecordHeader
128      * Note(erh): this sadly cannot be removed.
129      * A MmapV1RecordHeader DiskLoc has an offset from a file, while a RecordStore really wants an
130      * offset from an extent.  This intrinsically links an original record store to the original
131      * extent manager.
132      */
133     MmapV1RecordHeader* recordForV1(const DiskLoc& loc) const;
134 
135     std::unique_ptr<RecordFetcher> recordNeedsFetch(const DiskLoc& loc) const;
136 
137     /**
138      * @param loc - has to be for a specific MmapV1RecordHeader (not an Extent)
139      * Note(erh) see comment on recordFor
140      */
141     Extent* extentForV1(const DiskLoc& loc) const;
142 
143     /**
144      * @param loc - has to be for a specific MmapV1RecordHeader (not an Extent)
145      * Note(erh) see comment on recordFor
146      */
147     DiskLoc extentLocForV1(const DiskLoc& loc) const;
148 
149     /**
150      * @param loc - has to be for a specific Extent
151      */
152     Extent* getExtent(const DiskLoc& loc, bool doSanityCheck = true) const;
153 
154     /**
155      * Not thread safe, requires a database exclusive lock
156      */
157     DataFileVersion getFileFormat(OperationContext* opCtx) const final;
158     void setFileFormat(OperationContext* opCtx, DataFileVersion newVersion) final;
159 
getOpenFile(int n)160     const DataFile* getOpenFile(int n) const final {
161         return _getOpenFile(n);
162     }
163 
164     virtual int maxSize() const;
165 
166     virtual CacheHint* cacheHint(const DiskLoc& extentLoc, const HintType& hint);
167 
168 private:
169     /**
170      * will return NULL if nothing suitable in free list
171      */
172     DiskLoc _allocFromFreeList(OperationContext* opCtx, int approxSize, bool capped);
173 
174     /* allocate a new Extent, does not check free list
175     */
176     DiskLoc _createExtent(OperationContext* opCtx, int approxSize, bool enforceQuota);
177 
178     DataFile* _addAFile(OperationContext* opCtx, int sizeNeeded, bool preallocateNextFile);
179 
180 
181     /**
182      * Shared record retrieval logic used by the public recordForV1() and likelyInPhysicalMem()
183      * above.
184      */
185     MmapV1RecordHeader* _recordForV1(const DiskLoc& loc) const;
186 
187     DiskLoc _getFreeListStart() const;
188     DiskLoc _getFreeListEnd() const;
189     void _setFreeListStart(OperationContext* opCtx, DiskLoc loc);
190     void _setFreeListEnd(OperationContext* opCtx, DiskLoc loc);
191 
192     const DataFile* _getOpenFile(int fileId) const;
193     DataFile* _getOpenFile(int fileId);
194 
195     DiskLoc _createExtentInFile(
196         OperationContext* opCtx, int fileNo, DataFile* f, int size, bool enforceQuota);
197 
198     boost::filesystem::path _fileName(int n) const;
199 
200     // -----
201 
202     const std::string _dbname;  // i.e. "test"
203     const std::string _path;    // i.e. "/data/db"
204     const bool _directoryPerDB;
205     const ResourceId _rid;
206 
207     // This reference points into the MMAPv1 engine and is only valid as long as the
208     // engine is valid. Not owned here.
209     RecordAccessTracker* _recordAccessTracker;
210 
211     /**
212      * Simple wrapper around an array object to allow append-only modification of the array,
213      * as well as concurrent read-accesses. This class has a minimal interface to keep
214      * implementation simple and easy to modify.
215      */
216     class FilesArray {
217     public:
FilesArray()218         FilesArray() : _size(0) {}
219         ~FilesArray();
220 
221         /**
222          * Must be called before destruction.
223          */
224         void close(OperationContext* opCtx);
225 
226         /**
227          * Returns file at location 'n' in the array, with 'n' less than number of files added.
228          * Will always return the same pointer for a given file.
229          */
230         DataFile* operator[](int n) const {
231             invariant(n >= 0 && n < size());
232             return _files[n];
233         }
234 
235         /**
236          * Returns true iff no files were added
237          */
empty()238         bool empty() const {
239             return size() == 0;
240         }
241 
242         /**
243          * Returns number of files added to the array
244          */
size()245         int size() const {
246             return _size.load();
247         }
248 
249         // Appends val to the array, taking ownership of its pointer
250         void push_back(DataFile* val);
251 
252     private:
253         stdx::mutex _writersMutex;
254         AtomicInt32 _size;  // number of files in the array
255         DataFile* _files[DiskLoc::MaxFiles];
256     };
257 
258     FilesArray _files;
259 };
260 }
261