Skip to content

Commit

Permalink
Merge pull request juju#270 from axw/lp1338179-local-oplog
Browse files Browse the repository at this point in the history
Specify a smaller mongo oplog size on local provider

Since the local provider has a single Mongo instance,
the oplog is unuseful. We reduce the size to speed up
preallocation.

Fixes https://bugs.launchpad.net/juju-core/+bug/1338179
  • Loading branch information
jujubot committed Jul 8, 2014
2 parents 35ad036 + 7488b95 commit d47ac91
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 48 deletions.
1 change: 1 addition & 0 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const (
StorageDir = "STORAGE_DIR"
StorageAddr = "STORAGE_ADDR"
AgentServiceName = "AGENT_SERVICE_NAME"
MongoOplogSize = "MONGO_OPLOG_SIZE"
)

// The Config interface is the sole way that the agent gets access to the
Expand Down
2 changes: 1 addition & 1 deletion cmd/jujud/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ func (s *agentSuite) SetUpSuite(c *gc.C) {
// a bit when some tests are restarting every 50ms for 10 seconds,
// so use a slightly more friendly delay.
worker.RestartDelay = 250 * time.Millisecond
s.PatchValue(&ensureMongoServer, func(string, string, params.StateServingInfo) error {
s.PatchValue(&ensureMongoServer, func(mongo.EnsureServerParams) error {
return nil
})
}
Expand Down
38 changes: 33 additions & 5 deletions cmd/jujud/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/base64"
"fmt"
"net"
"strconv"
"time"

"github.com/juju/cmd"
Expand Down Expand Up @@ -159,6 +160,33 @@ func (c *BootstrapCommand) Run(_ *cmd.Context) error {
return m.SetHasVote(true)
}

// newEnsureServerParams creates an EnsureServerParams from an agent configuration.
func newEnsureServerParams(agentConfig agent.Config) (mongo.EnsureServerParams, error) {
// If oplog size is specified in the agent configuration, use that.
// Otherwise leave the default zero value to indicate to EnsureServer
// that it should calculate the size.
var oplogSize int
if oplogSizeString := agentConfig.Value(agent.MongoOplogSize); oplogSizeString != "" {
var err error
if oplogSize, err = strconv.Atoi(oplogSizeString); err != nil {
return mongo.EnsureServerParams{}, fmt.Errorf("invalid oplog size: %q", oplogSizeString)
}
}

servingInfo, ok := agentConfig.StateServingInfo()
if !ok {
return mongo.EnsureServerParams{}, fmt.Errorf("agent config has no state serving info")
}

params := mongo.EnsureServerParams{
StateServingInfo: servingInfo,
DataDir: agentConfig.DataDir(),
Namespace: agentConfig.Value(agent.Namespace),
OplogSize: oplogSize,
}
return params, nil
}

func (c *BootstrapCommand) startMongo(addrs []network.Address, agentConfig agent.Config) error {
logger.Debugf("starting mongo")

Expand Down Expand Up @@ -186,11 +214,11 @@ func (c *BootstrapCommand) startMongo(addrs []network.Address, agentConfig agent
}

logger.Debugf("calling ensureMongoServer")
err = ensureMongoServer(
agentConfig.DataDir(),
agentConfig.Value(agent.Namespace),
servingInfo,
)
ensureServerParams, err := newEnsureServerParams(agentConfig)
if err != nil {
return err
}
err = ensureMongoServer(ensureServerParams)
if err != nil {
return err
}
Expand Down
22 changes: 19 additions & 3 deletions cmd/jujud/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type BootstrapSuite struct {
instanceId instance.Id
dataDir string
logDir string
mongoOplogSize string
fakeEnsureMongo fakeEnsure
bootstrapName string
}
Expand All @@ -58,14 +59,15 @@ type fakeEnsure struct {
initiateCount int
dataDir string
namespace string
oplogSize int
info params.StateServingInfo
initiateParams peergrouper.InitiateMongoParams
err error
}

func (f *fakeEnsure) fakeEnsureMongo(dataDir, namespace string, info params.StateServingInfo) error {
func (f *fakeEnsure) fakeEnsureMongo(args mongo.EnsureServerParams) error {
f.ensureCount++
f.dataDir, f.namespace, f.info = dataDir, namespace, info
f.dataDir, f.namespace, f.info, f.oplogSize = args.DataDir, args.Namespace, args.StateServingInfo, args.OplogSize
return f.err
}

Expand Down Expand Up @@ -95,6 +97,7 @@ func (s *BootstrapSuite) SetUpTest(c *gc.C) {
s.MgoSuite.SetUpTest(c)
s.dataDir = c.MkDir()
s.logDir = c.MkDir()
s.mongoOplogSize = "1234"
s.fakeEnsureMongo = fakeEnsure{}
}

Expand Down Expand Up @@ -129,7 +132,10 @@ func (s *BootstrapSuite) initBootstrapCommand(c *gc.C, jobs []params.MachineJob,
StateAddresses: []string{gitjujutesting.MgoServer.Addr()},
APIAddresses: []string{"0.1.2.3:1234"},
CACert: testing.CACert,
Values: map[string]string{agent.Namespace: "foobar"},
Values: map[string]string{
agent.Namespace: "foobar",
agent.MongoOplogSize: s.mongoOplogSize,
},
}
servingInfo := params.StateServingInfo{
Cert: "some cert",
Expand Down Expand Up @@ -160,6 +166,7 @@ func (s *BootstrapSuite) TestInitializeEnvironment(c *gc.C) {
c.Assert(s.fakeEnsureMongo.initiateCount, gc.Equals, 1)
c.Assert(s.fakeEnsureMongo.ensureCount, gc.Equals, 1)
c.Assert(s.fakeEnsureMongo.dataDir, gc.Equals, s.dataDir)
c.Assert(s.fakeEnsureMongo.oplogSize, gc.Equals, 1234)

expectInfo, exists := machConf.StateServingInfo()
c.Assert(exists, jc.IsTrue)
Expand Down Expand Up @@ -205,6 +212,15 @@ func (s *BootstrapSuite) TestInitializeEnvironment(c *gc.C) {
c.Assert(&cons, jc.Satisfies, constraints.IsEmpty)
}

func (s *BootstrapSuite) TestInitializeEnvironmentInvalidOplogSize(c *gc.C) {
s.mongoOplogSize = "NaN"
hw := instance.MustParseHardware("arch=amd64 mem=8G")
_, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", s.envcfg, "--instance-id", string(s.instanceId), "--hardware", hw.String())
c.Assert(err, gc.IsNil)
err = cmd.Run(nil)
c.Assert(err, gc.ErrorMatches, `invalid oplog size: "NaN"`)
}

func (s *BootstrapSuite) TestSetConstraints(c *gc.C) {
tcons := constraints.Value{Mem: uint64p(2048), CpuCores: uint64p(2)}
_, cmd, err := s.initBootstrapCommand(c, nil,
Expand Down
11 changes: 5 additions & 6 deletions cmd/jujud/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,6 @@ func (a *MachineAgent) ensureMongoServer(agentConfig agent.Config) error {
if !ok {
return fmt.Errorf("state worker was started with no state serving info")
}
namespace := agentConfig.Value(agent.Namespace)

// When upgrading from a pre-HA-capable environment,
// we must add machine-0 to the admin database and
Expand Down Expand Up @@ -598,11 +597,11 @@ func (a *MachineAgent) ensureMongoServer(agentConfig agent.Config) error {
}

// ensureMongoServer installs/upgrades the upstart config as necessary.
if err := ensureMongoServer(
agentConfig.DataDir(),
namespace,
servingInfo,
); err != nil {
ensureServerParams, err := newEnsureServerParams(agentConfig)
if err != nil {
return err
}
if err := ensureMongoServer(ensureServerParams); err != nil {
return err
}
if !shouldInitiateMongoServer {
Expand Down
2 changes: 1 addition & 1 deletion mongo/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var (
MaxOplogSizeMB = &maxOplogSizeMB
PreallocFile = &preallocFile

OplogSize = oplogSize
DefaultOplogSize = defaultOplogSize
FsAvailSpace = fsAvailSpace
PreallocFileSizes = preallocFileSizes
PreallocFiles = preallocFiles
Expand Down
52 changes: 41 additions & 11 deletions mongo/mongo.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os/exec"
"path"
"path/filepath"
"strconv"

"github.com/juju/loggo"
"github.com/juju/utils"
Expand Down Expand Up @@ -134,7 +135,25 @@ func RemoveService(namespace string) error {
return upstartServiceStopAndRemove(svc)
}

// EnsureMongoServer ensures that the correct mongo upstart script is installed
// EnsureServerParams is a parameter struct for EnsureServer.
type EnsureServerParams struct {
params.StateServingInfo

// DataDir is the machine agent data directory.
DataDir string

// Namespace is the machine agent's namespace, which is used to
// generate a unique service name for Mongo.
Namespace string

// OplogSize is the size of the Mongo oplog.
// If this is zero, then EnsureServer will
// calculate a default size according to the
// algorithm defined in Mongo.
OplogSize int
}

// EnsureServer ensures that the correct mongo upstart script is installed
// and running.
//
// This method will remove old versions of the mongo upstart script as necessary
Expand All @@ -143,21 +162,24 @@ func RemoveService(namespace string) error {
// The namespace is a unique identifier to prevent multiple instances of mongo
// on this machine from colliding. This should be empty unless using
// the local provider.
func EnsureServer(dataDir string, namespace string, info params.StateServingInfo) error {
logger.Infof("Ensuring mongo server is running; data directory %s; port %d", dataDir, info.StatePort)
dbDir := filepath.Join(dataDir, "db")
func EnsureServer(args EnsureServerParams) error {
logger.Infof(
"Ensuring mongo server is running; data directory %s; port %d",
args.DataDir, args.StatePort,
)
dbDir := filepath.Join(args.DataDir, "db")

if err := os.MkdirAll(dbDir, 0700); err != nil {
return fmt.Errorf("cannot create mongo database directory: %v", err)
}

certKey := info.Cert + "\n" + info.PrivateKey
err := utils.AtomicWriteFile(sslKeyPath(dataDir), []byte(certKey), 0600)
certKey := args.Cert + "\n" + args.PrivateKey
err := utils.AtomicWriteFile(sslKeyPath(args.DataDir), []byte(certKey), 0600)
if err != nil {
return fmt.Errorf("cannot write SSL key: %v", err)
}

err = utils.AtomicWriteFile(sharedSecretPath(dataDir), []byte(info.SharedSecret), 0600)
err = utils.AtomicWriteFile(sharedSecretPath(args.DataDir), []byte(args.SharedSecret), 0600)
if err != nil {
return fmt.Errorf("cannot write mongod shared secret: %v", err)
}
Expand All @@ -180,7 +202,14 @@ func EnsureServer(dataDir string, namespace string, info params.StateServingInfo
return fmt.Errorf("cannot install mongod: %v", err)
}

upstartConf, mongoPath, err := upstartService(namespace, dataDir, dbDir, info.StatePort)
oplogSizeMB := args.OplogSize
if oplogSizeMB == 0 {
if oplogSizeMB, err = defaultOplogSize(dbDir); err != nil {
return err
}
}

upstartConf, mongoPath, err := upstartService(args.Namespace, args.DataDir, dbDir, args.StatePort, oplogSizeMB)
if err != nil {
return err
}
Expand All @@ -192,7 +221,7 @@ func EnsureServer(dataDir string, namespace string, info params.StateServingInfo
if err := makeJournalDirs(dbDir); err != nil {
return fmt.Errorf("error creating journal directories: %v", err)
}
if err := preallocOplog(dbDir); err != nil {
if err := preallocOplog(dbDir, oplogSizeMB); err != nil {
return fmt.Errorf("error creating oplog files: %v", err)
}
return upstartConfInstall(upstartConf)
Expand Down Expand Up @@ -242,7 +271,7 @@ func sharedSecretPath(dataDir string) string {
// upstartService returns the upstart config for the mongo state service.
// It also returns the path to the mongod executable that the upstart config
// will be using.
func upstartService(namespace, dataDir, dbDir string, port int) (*upstart.Conf, string, error) {
func upstartService(namespace, dataDir, dbDir string, port, oplogSizeMB int) (*upstart.Conf, string, error) {
svc := upstart.NewService(ServiceName(namespace))

mongoPath, err := Path()
Expand All @@ -262,7 +291,8 @@ func upstartService(namespace, dataDir, dbDir string, port int) (*upstart.Conf,
" --journal" +
" --keyFile " + utils.ShQuote(sharedSecretPath(dataDir)) +
" --replSet " + ReplicaSetName +
" --ipv6"
" --ipv6 " +
" --oplogSize " + strconv.Itoa(oplogSizeMB)
conf := &upstart.Conf{
Service: *svc,
Desc: "juju state database",
Expand Down
28 changes: 18 additions & 10 deletions mongo/mongo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ var testInfo = params.StateServingInfo{
SharedSecret: "foobar-sharedsecret",
}

func makeEnsureServerParams(dataDir, namespace string) mongo.EnsureServerParams {
return mongo.EnsureServerParams{
StateServingInfo: testInfo,
DataDir: dataDir,
Namespace: namespace,
}
}

func (s *MongoSuite) SetUpTest(c *gc.C) {
s.BaseSuite.SetUpTest(c)
// Try to make sure we don't execute any commands accidentally.
Expand Down Expand Up @@ -131,7 +139,7 @@ func (s *MongoSuite) TestEnsureServer(c *gc.C) {

mockShellCommand(c, &s.CleanupSuite, "apt-get")

err := mongo.EnsureServer(dataDir, namespace, testInfo)
err := mongo.EnsureServer(makeEnsureServerParams(dataDir, namespace))
c.Assert(err, gc.IsNil)

testJournalDirs(dbDir, c)
Expand Down Expand Up @@ -162,7 +170,7 @@ func (s *MongoSuite) TestEnsureServer(c *gc.C) {

s.installed = nil
// now check we can call it multiple times without error
err = mongo.EnsureServer(dataDir, namespace, testInfo)
err = mongo.EnsureServer(makeEnsureServerParams(dataDir, namespace))
c.Assert(err, gc.IsNil)
assertInstalled()

Expand Down Expand Up @@ -198,7 +206,7 @@ func (s *MongoSuite) TestInstallMongod(c *gc.C) {

s.PatchValue(&version.Current.Series, test.series)

err := mongo.EnsureServer(dataDir, namespace, testInfo)
err := mongo.EnsureServer(makeEnsureServerParams(dataDir, namespace))
c.Assert(err, gc.IsNil)

cmds := getMockShellCalls(c, output)
Expand All @@ -219,23 +227,23 @@ func (s *MongoSuite) TestInstallMongod(c *gc.C) {
func (s *MongoSuite) TestUpstartServiceWithReplSet(c *gc.C) {
dataDir := c.MkDir()

svc, _, err := mongo.UpstartService("", dataDir, dataDir, 1234)
svc, _, err := mongo.UpstartService("", dataDir, dataDir, 1234, 1024)
c.Assert(err, gc.IsNil)
c.Assert(strings.Contains(svc.Cmd, "--replSet"), jc.IsTrue)
}

func (s *MongoSuite) TestUpstartServiceIPv6(c *gc.C) {
dataDir := c.MkDir()

svc, _, err := mongo.UpstartService("", dataDir, dataDir, 1234)
svc, _, err := mongo.UpstartService("", dataDir, dataDir, 1234, 1024)
c.Assert(err, gc.IsNil)
c.Assert(strings.Contains(svc.Cmd, "--ipv6"), jc.IsTrue)
}

func (s *MongoSuite) TestUpstartServiceWithJournal(c *gc.C) {
dataDir := c.MkDir()

svc, _, err := mongo.UpstartService("", dataDir, dataDir, 1234)
svc, _, err := mongo.UpstartService("", dataDir, dataDir, 1234, 1024)
c.Assert(err, gc.IsNil)
journalPresent := strings.Contains(svc.Cmd, " --journal ") || strings.HasSuffix(svc.Cmd, " --journal")
c.Assert(journalPresent, jc.IsTrue)
Expand Down Expand Up @@ -273,11 +281,11 @@ func (s *MongoSuite) TestQuantalAptAddRepo(c *gc.C) {
// test that we call add-apt-repository only for quantal (and that if it
// fails, we return the error)
s.PatchValue(&version.Current.Series, "quantal")
err := mongo.EnsureServer(dir, "", testInfo)
err := mongo.EnsureServer(makeEnsureServerParams(dir, ""))
c.Assert(err, gc.ErrorMatches, "cannot install mongod: cannot add apt repository: exit status 1.*")

s.PatchValue(&version.Current.Series, "trusty")
err = mongo.EnsureServer(dir, "", testInfo)
err = mongo.EnsureServer(makeEnsureServerParams(dir, ""))
c.Assert(err, gc.IsNil)
}

Expand All @@ -286,7 +294,7 @@ func (s *MongoSuite) TestNoMongoDir(c *gc.C) {
// created.
mockShellCommand(c, &s.CleanupSuite, "apt-get")
dataDir := filepath.Join(c.MkDir(), "dir", "data")
err := mongo.EnsureServer(dataDir, "", testInfo)
err := mongo.EnsureServer(makeEnsureServerParams(dataDir, ""))
c.Check(err, gc.IsNil)

_, err = os.Stat(filepath.Join(dataDir, "db"))
Expand Down Expand Up @@ -352,7 +360,7 @@ func (s *MongoSuite) TestAddPPAInQuantal(c *gc.C) {
s.PatchValue(&version.Current.Series, "quantal")

dataDir := c.MkDir()
err := mongo.EnsureServer(dataDir, "", testInfo)
err := mongo.EnsureServer(makeEnsureServerParams(dataDir, ""))
c.Assert(err, gc.IsNil)

c.Assert(getMockShellCalls(c, addAptRepoOut), gc.DeepEquals, [][]string{{
Expand Down
Loading

0 comments on commit d47ac91

Please sign in to comment.