// DumpOplogAfterTimestamp takes a timestamp and writer and dumps all oplog entries after // the given timestamp to the writer. Returns any errors that occur. func (dump *MongoDump) DumpOplogAfterTimestamp(ts bson.MongoTimestamp) error { session, err := dump.sessionProvider.GetSession() if err != nil { return err } defer session.Close() session.SetPrefetch(1.0) // mimic exhaust cursor queryObj := bson.M{"ts": bson.M{"$gt": ts}} oplogQuery := session.DB("local").C(dump.oplogCollection).Find(queryObj).LogReplay() oplogCount, err := dump.dumpQueryToIntent(oplogQuery, dump.manager.Oplog(), dump.getResettableOutputBuffer()) if err == nil { log.Logvf(log.Always, "\tdumped %v oplog %v", oplogCount, util.Pluralize(int(oplogCount), "entry", "entries")) } return err }
// DumpOplogAfterTimestamp takes a timestamp and writer and dumps all oplog entries after // the given timestamp to the writer. Returns any errors that occur. func (dump *MongoDump) DumpOplogAfterTimestamp(ts bson.MongoTimestamp) error { session, err := dump.sessionProvider.GetSession() if err != nil { return err } defer session.Close() intent := dump.manager.Oplog() err = intent.BSONFile.Open() if err != nil { return fmt.Errorf("error opening output stream for dumping oplog: %v", err) } defer intent.BSONFile.Close() session.SetPrefetch(1.0) // mimic exhaust cursor queryObj := bson.M{"ts": bson.M{"$gt": ts}} oplogQuery := session.DB("local").C(dump.oplogCollection).Find(queryObj).LogReplay() oplogCount, err := dump.dumpQueryToWriter(oplogQuery, dump.manager.Oplog()) if err == nil { log.Logvf(log.Always, "\tdumped %v oplog %v", oplogCount, util.Pluralize(int(oplogCount), "entry", "entries")) } return err }
// RestoreIntent attempts to restore a given intent into MongoDB. func (restore *MongoRestore) RestoreIntent(intent *intents.Intent) error { collectionExists, err := restore.CollectionExists(intent) if err != nil { return fmt.Errorf("error reading database: %v", err) } if restore.safety == nil && !restore.OutputOptions.Drop && collectionExists { log.Logvf(log.Always, "restoring to existing collection %v without dropping", intent.Namespace()) log.Logv(log.Always, "Important: restored data will be inserted without raising errors; check your server log") } if restore.OutputOptions.Drop { if collectionExists { if strings.HasPrefix(intent.C, "system.") { log.Logvf(log.Always, "cannot drop system collection %v, skipping", intent.Namespace()) } else { log.Logvf(log.Info, "dropping collection %v before restoring", intent.Namespace()) err = restore.DropCollection(intent) if err != nil { return err // no context needed } collectionExists = false } } else { log.Logvf(log.DebugLow, "collection %v doesn't exist, skipping drop command", intent.Namespace()) } } var options bson.D var indexes []IndexDocument // get indexes from system.indexes dump if we have it but don't have metadata files if intent.MetadataFile == nil { if _, ok := restore.dbCollectionIndexes[intent.DB]; ok { if indexes, ok = restore.dbCollectionIndexes[intent.DB][intent.C]; ok { log.Logvf(log.Always, "no metadata; falling back to system.indexes") } } } logMessageSuffix := "with no metadata" // first create the collection with options from the metadata file if intent.MetadataFile != nil { logMessageSuffix = "using options from metadata" err = intent.MetadataFile.Open() if err != nil { return err } defer intent.MetadataFile.Close() log.Logvf(log.Always, "reading metadata for %v from %v", intent.Namespace(), intent.MetadataLocation) metadata, err := ioutil.ReadAll(intent.MetadataFile) if err != nil { return fmt.Errorf("error reading metadata from %v: %v", intent.MetadataLocation, err) } options, indexes, err = restore.MetadataFromJSON(metadata) if err != nil { return fmt.Errorf("error parsing metadata from %v: %v", intent.MetadataLocation, err) } if restore.OutputOptions.NoOptionsRestore { log.Logv(log.Info, "not restoring collection options") logMessageSuffix = "with no collection options" options = nil } } if !collectionExists { log.Logvf(log.Info, "creating collection %v %s", intent.Namespace(), logMessageSuffix) log.Logvf(log.DebugHigh, "using collection options: %#v", options) err = restore.CreateCollection(intent, options) if err != nil { return fmt.Errorf("error creating collection %v: %v", intent.Namespace(), err) } } else { log.Logvf(log.Info, "collection %v already exists - skipping collection create", intent.Namespace()) } var documentCount int64 if intent.BSONFile != nil { err = intent.BSONFile.Open() if err != nil { return err } defer intent.BSONFile.Close() log.Logvf(log.Always, "restoring %v from %v", intent.Namespace(), intent.Location) bsonSource := db.NewDecodedBSONSource(db.NewBSONSource(intent.BSONFile)) defer bsonSource.Close() documentCount, err = restore.RestoreCollectionToDB(intent.DB, intent.C, bsonSource, intent.BSONFile, intent.Size) if err != nil { return fmt.Errorf("error restoring from %v: %v", intent.Location, err) } } // finally, add indexes if len(indexes) > 0 && !restore.OutputOptions.NoIndexRestore { log.Logvf(log.Always, "restoring indexes for collection %v from metadata", intent.Namespace()) err = restore.CreateIndexes(intent, indexes) if err != nil { return fmt.Errorf("error creating indexes for %v: %v", intent.Namespace(), err) } } else { log.Logv(log.Always, "no indexes to restore") } log.Logvf(log.Always, "finished restoring %v (%v %v)", intent.Namespace(), documentCount, util.Pluralize(int(documentCount), "document", "documents")) return nil }
// docPlural returns "document" or "documents" depending on the // count of documents passed in. func docPlural(count int64) string { return util.Pluralize(int(count), "document", "documents") }