0

In my environments I can have DB of 5-10 GB or DB of 10 TB (video recordings).
Focusing on the 5-10 GB: if I keep default settings for prealloc an small-files I can actually loose 20-40% of the disk space because of allocations.
In my production environments, the disk size can be 512G, but user can limit DB allocation to only 10G.

To implement this, I have a scheduled task that deletes the old documents from the DB when DB dataSize reached a certain threshold.

I can't use capped-collection (GridFS, sharding limitation, cannot delete random documents..), I can't use --no-prealloc/small-files flags, cause i need the files insert to be efficient.

So what happens, is this: if dataSize gets to 10G, the fileSize would be at least 12G, so I need to take that in consideration and lower the threshold in 2GB (and lose a lot of disk space).

What I do want, is to tell mongo to pre-allocate all the 10 GB the user requested, and disable further pre-alloc.

For example, running mongod with --no-prealloc and --small-files, but pre-allocate in advance all the 10 GB.

Another protection I gain here, is protecting the user against sudden disk-full errors. If he regularly downloads Game of Thrones episodes to the same drive, he can't take space from the DB 10G, since it's already pre-allocated.

(using C# driver)

RoeeK
  • 1,112
  • 12
  • 23
  • I appreciate a lot, if the 'close' voter could share with me some knowledge. otherwise, it isn't going to improve my next questions whatsoever. – RoeeK May 20 '14 at 13:14

2 Answers2

0

The following applies to regular collections as per documentation. But since metadata can be attached to files, it might very well apply to GridFS as well.

MongoDB uses what is called a record to store data. A record consists of two parts: the actual data and something which is called "padding". The padding is basically unused data which is used if the document grows in size. The reason for that is that a document or file chunk in GridFS respectively never gets fragmented to enhance query performance. So what would happen when the document or a file chunk grows in size is that it had to be moved to a different location in the datafile(s) every time the file is modified, which can be a very costly operation in terms of IO and time. So with the default settings, if the document or file chunk grows in size is that the padding is used instead of moving the file, thus reducing the need of moving around data in the data file and thereby improving performance. Only if the growth of the data exceeds the preallocated padding the document or file chunk is moved within the datafile(s).

The default strategy for preallocating padding space is "usePowerOf2Sizes", which determines the padding size by taking the document size and uses the next power of two size as the size preallocated for the document. Say we have a 47 byte document, the usePowerOf2Sizes strategy would preallocate 64 bytes for that document, resulting in a padding of 17 bytes. There is another preallocation strategy, however. It is called "exactFit". It determines the padding space by multiplying the document size with a dynamically computed "paddingFactor". As far as I understood, the padding factor is determined by the average document growth in the respective collection. Since we are talking of static files in your case, the padding factor should always be 0, and because of this, there should not be any "lost" space any more.

So I think a possible solution would be to change the allocation strategy for both the files and the chunks collection to exactFit. Could you try that and share your findings with us?

Markus W Mahlberg
  • 19,711
  • 6
  • 65
  • 89
  • 1
    few months ago, my DB dataSize was about 10G while the fileSize was more than 20G. then, I applied usePowerOf2Sizes and its fixed the fragmentation issue. the difference is now is 2-3G which is basically because of the pre-allocation and not the padding/fragmentation problem. exactFit might improve the padding ratio, but i'd still be left with the 2 pre-allocated GB. – RoeeK May 20 '14 at 11:09
  • The difference should not come from fragmentation. What happens ist that space, once allocated is returned to the filesystem in only one case: "repairDatabase" is called (see [repairDatabase docs](http://docs.mongodb.org/manual/reference/command/repairDatabase/#using-repairdatabase-to-reclaim-disk-space) for details. So even if you delete old docs, the place is not returned until you call "repairDatabase". But to get that straight: to which values are you referring? Is it dataSize, storageSize or fileSize? – Markus W Mahlberg May 20 '14 at 11:32
  • exactly, I don't need allocated space return to filesystem (thanks to the usePowerOf2Sizes). i refer to dataSize and fileSize. lets say, i want to max fileSize of 10G. so if i wait for the dataSize to be 10G, then delete 1G, then the fileSize would already be at least 12G. if i wait for the dataSize to be 8G, then delete old documents, then i lose 2G. I want to stop allocate more disk space at a certain point. i want dataSize and fileSize to be around the 10G (in optimal document sizes - all same size), and stop allocate more. or like i said - pre-allocate all the 10G in advance. – RoeeK May 20 '14 at 12:34
  • I do not think that dataSize can be equal to fileSize. While fileSize includes the indices, dataSize does not. What happens if you adjust the `scale` parameter to 1024? – Markus W Mahlberg May 20 '14 at 13:14
  • of course they can't be equal. i'm talking completely on another thing. fileSize will always be bigger than dataSize. using powerOf2 and other techniques i can improve the REUSE of the space. I'm not talking about reusing the space better, but instead, how can I prevent the extra pre-allocated 2G I ain't gonna use (cause i need only 10G, then i delete old documents so it reuse them, i don't wan't it to allocate another 2G file). – RoeeK May 20 '14 at 13:21
  • Ah, now I got it. Well, I do not think that this is possible as per [the algorithm which is used for preallocation](http://docs.mongodb.org/manual/faq/storage/#why-are-the-files-in-my-data-directory-larger-than-the-data-in-my-database). So if your size exceeds 10,176 Mb (32 + 64 + 128 + 256 + 512 + 1,024 + 2,048 + 2,048 + 2,048 + 2,048), a new datafile of 2Gb will be allocated - no matter what. The only way I can think of to achieve what you want to do is to implement a hard quota on the application side. Please keep in mind that there is a limit on how many collections a DB can have. – Markus W Mahlberg May 20 '14 at 13:46
  • OR... i didn't test it.. but what's going to happen if i allocate myself mongo files, and then start is with --no-prealloc. or maybe, change its prealloc settings when fileSize reaches 10G... what do you think? – RoeeK May 20 '14 at 13:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/54045/discussion-between-roeek-and-markus-w-mahlberg). – RoeeK May 20 '14 at 14:06
0

I think I found a solution: You might want to look at the --quota and --quotafiles command line opts. In your case, you also might want to add the --smalfiles option. So

mongod --smallfiles --quota --quotafiles 11

should give you a size of exactly 10224 MB for your data, which, adding the default namespace file size of 16MB equals your target size of 10GB, excluding indices.

Markus W Mahlberg
  • 19,711
  • 6
  • 65
  • 89
  • sounds like an answer! I think it might be the first problem with mongo I meet, that actually have a descent solution. (note to mongo guys: please change your memory management model) – RoeeK May 22 '14 at 15:38
  • Keep in mind though that you still have to check the current size preInsert and implement a strategy for deciding which old files to delete. – Markus W Mahlberg May 22 '14 at 15:48