@@ -476,6 +476,23 @@ func (db *DB) mmap(minsz int) (err error) {
476476 return err
477477 }
478478
479+ if db .MaxSize > 0 && size > db .MaxSize {
480+ // On Windows, the data file is expanded to the full mapped size during mmap,
481+ // so we must reject any mmap size larger than the configured max size.
482+ //
483+ // On other platforms, mmap itself does not grow the file immediately, so the
484+ // mapped size may exceed the max size temporarily. The file size limit is
485+ // enforced later when the file actually grows.
486+ //
487+ // In practice, this check mainly applies when opening a database with a large
488+ // InitialMmapSize. In all other cases, file growth is already guarded during
489+ // page allocation.
490+ if size > fileSize && runtime .GOOS == "windows" {
491+ db .Logger ().Errorf ("[GOOS: %s, GOARCH: %s] maximum db size reached, size: %d, db.MaxSize: %d" , runtime .GOOS , runtime .GOARCH , size , db .MaxSize )
492+ return berrors .ErrMaxSizeReached
493+ }
494+ }
495+
479496 if db .Mlock {
480497 // Unlock db memory
481498 if err := db .munlock (fileSize ); err != nil {
@@ -1165,6 +1182,26 @@ func (db *DB) allocate(txid common.Txid, count int) (*common.Page, error) {
11651182 // Resize mmap() if we're at the end.
11661183 p .SetId (db .rwtx .meta .Pgid ())
11671184 var minsz = int ((p .Id ()+ common .Pgid (count ))+ 1 ) * db .pageSize
1185+ if db .MaxSize > 0 {
1186+ nextAllocSize := minsz
1187+ nextMmapSize , err := db .mmapSize (minsz )
1188+ if err != nil {
1189+ return nil , fmt .Errorf ("mmap size calculation error: %w" , err )
1190+ }
1191+ if runtime .GOOS == "windows" {
1192+ // nextAllocSize may not exactly match nextMmapSize.
1193+ // On Windows, this mismatch may cause the file size to slightly exceed maxSize,
1194+ // while it is harmless on other platforms.
1195+ nextAllocSize = nextMmapSize
1196+ } else {
1197+ // On non-Windows platforms, the database file is only grown explicitly in grow calls.
1198+ nextAllocSize = db .growSize (nextMmapSize , nextAllocSize )
1199+ }
1200+ if nextAllocSize > db .MaxSize {
1201+ db .Logger ().Errorf ("[GOOS: %s, GOARCH: %s] maximum db size reached, minSize: %d (allocSize: %d), db.MaxSize: %d" , runtime .GOOS , runtime .GOARCH , minsz , nextAllocSize , db .MaxSize )
1202+ return nil , berrors .ErrMaxSizeReached
1203+ }
1204+ }
11681205 if minsz >= db .datasz {
11691206 if err := db .mmap (minsz ); err != nil {
11701207 if err == berrors .ErrMaxSizeReached {
@@ -1195,18 +1232,7 @@ func (db *DB) grow(sz int) error {
11951232 return nil
11961233 }
11971234
1198- // If the data is smaller than the alloc size then only allocate what's needed.
1199- // Once it goes over the allocation size then allocate in chunks.
1200- if db .datasz <= db .AllocSize {
1201- sz = db .datasz
1202- } else {
1203- sz += db .AllocSize
1204- }
1205-
1206- if ! db .readOnly && db .MaxSize > 0 && sz > db .MaxSize {
1207- lg .Errorf ("[GOOS: %s, GOARCH: %s] maximum db size reached, size: %d, db.MaxSize: %d" , runtime .GOOS , runtime .GOARCH , sz , db .MaxSize )
1208- return berrors .ErrMaxSizeReached
1209- }
1235+ sz = db .growSize (db .datasz , sz )
12101236
12111237 // Truncate and fsync to ensure file size metadata is flushed.
12121238 // https://github.com/boltdb/bolt/issues/284
@@ -1234,6 +1260,16 @@ func (db *DB) grow(sz int) error {
12341260 return nil
12351261}
12361262
1263+ func (db * DB ) growSize (mmapSize , growSize int ) int {
1264+ // If the data is smaller than the alloc size then only allocate what's needed.
1265+ // Once it goes over the allocation size then allocate in chunks.
1266+ if mmapSize <= db .AllocSize {
1267+ return mmapSize
1268+ } else {
1269+ return growSize + db .AllocSize
1270+ }
1271+ }
1272+
12371273func (db * DB ) IsReadOnly () bool {
12381274 return db .readOnly
12391275}
@@ -1333,7 +1369,7 @@ type Options struct {
13331369 // PageSize overrides the default OS page size.
13341370 PageSize int
13351371
1336- // MaxSize sets the maximum size of the data file. <= 0 means no maximum .
1372+ // MaxSize sets the maximum size of the data file. A value <= 0 means no limit .
13371373 MaxSize int
13381374
13391375 // NoSync sets the initial value of DB.NoSync. Normally this can just be
0 commit comments