github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/mongo/prealloc.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package mongo
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"os/exec"
    10  	"runtime"
    11  	"strconv"
    12  	"strings"
    13  )
    14  
    15  var (
    16  	runtimeGOOS = runtime.GOOS
    17  
    18  	smallOplogSizeMB   = 512
    19  	regularOplogSizeMB = 1024
    20  	smallOplogBoundary = 15360.0
    21  
    22  	availSpace = fsAvailSpace
    23  )
    24  
    25  // defaultOplogSize returns the default size in MB for the
    26  // mongo oplog based on the directory of the mongo database.
    27  //
    28  // Since we limit the maximum oplog size to 1GB and every change
    29  // in opLogSize requires mongo restart we are not using the default
    30  // MongoDB formula but simply using 512MB for small disks and 1GB
    31  // for larger ones.
    32  func defaultOplogSize(dir string) (int, error) {
    33  	// "For 64-bit OS X systems, MongoDB allocates 183 megabytes of
    34  	// space to the oplog."
    35  	if runtimeGOOS == "darwin" {
    36  		return 183, nil
    37  	}
    38  
    39  	avail, err := availSpace(dir)
    40  	if err != nil {
    41  		return -1, err
    42  	}
    43  	if avail < smallOplogBoundary {
    44  		return smallOplogSizeMB, nil
    45  	} else {
    46  		return regularOplogSizeMB, nil
    47  	}
    48  }
    49  
    50  // fsAvailSpace returns the available space in MB on the
    51  // filesystem containing the specified directory.
    52  func fsAvailSpace(dir string) (avail float64, err error) {
    53  	var stderr bytes.Buffer
    54  	cmd := exec.Command("df", dir)
    55  	cmd.Stderr = &stderr
    56  	out, err := cmd.Output()
    57  	if err != nil {
    58  		err := fmt.Errorf("df failed: %v", err)
    59  		if stderr.Len() > 0 {
    60  			err = fmt.Errorf("%s (%q)", err, stderr.String())
    61  		}
    62  		return -1, err
    63  	}
    64  	lines := strings.Split(strings.TrimSpace(string(out)), "\n")
    65  	if len(lines) < 2 {
    66  		logger.Errorf("unexpected output: %q", out)
    67  		return -1, fmt.Errorf("could not determine available space on %q", dir)
    68  	}
    69  	fields := strings.Fields(lines[1])
    70  	if len(fields) < 4 {
    71  		logger.Errorf("unexpected output: %q", out)
    72  		return -1, fmt.Errorf("could not determine available space on %q", dir)
    73  	}
    74  	kilobytes, err := strconv.Atoi(fields[3])
    75  	if err != nil {
    76  		return -1, err
    77  	}
    78  	return float64(kilobytes) / 1024, err
    79  }