func TestShadowerPush(t *testing.T) { //base.LogKeys["Shadow"] = true bucket := makeExternalBucket() defer bucket.Close() db := setupTestDB(t) defer tearDownTestDB(t, db) var err error db.Shadower, err = NewShadower(db.DatabaseContext, bucket, nil) assertNoError(t, err, "NewShadower") key1rev1, err := db.Put("key1", Body{"aaa": "bbb"}) assertNoError(t, err, "Put") _, err = db.Put("key2", Body{"ccc": "ddd"}) assertNoError(t, err, "Put") base.Log("Waiting for shadower to catch up...") var doc1, doc2 Body waitFor(t, func() bool { return bucket.Get("key1", &doc1) == nil && bucket.Get("key2", &doc2) == nil }) assert.DeepEquals(t, doc1, Body{"aaa": "bbb"}) assert.DeepEquals(t, doc2, Body{"ccc": "ddd"}) base.Log("Deleting local doc") db.DeleteDoc("key1", key1rev1) waitFor(t, func() bool { err = bucket.Get("key1", &doc1) return err != nil }) assert.True(t, base.IsDocNotFoundError(err)) }
func TestShadowerPattern(t *testing.T) { bucket := makeExternalBucket() defer bucket.Close() bucket.Set("key1", 0, Body{"foo": 1}) bucket.Set("ignorekey", 0, Body{"bar": -1}) bucket.Set("key2", 0, Body{"bar": -1}) db := setupTestDB(t) defer tearDownTestDB(t, db) pattern, _ := regexp.Compile(`key\d+`) shadower, err := NewShadower(db.DatabaseContext, bucket, pattern) assertNoError(t, err, "NewShadower") defer shadower.Stop() base.Log("Waiting for shadower to catch up...") waitFor(t, func() bool { seq, _ := db.LastSequence() return seq >= 1 }) doc1, _ := db.GetDoc("key1") docI, _ := db.GetDoc("ignorekey") doc2, _ := db.GetDoc("key2") assert.DeepEquals(t, doc1.body, Body{"foo": float64(1)}) assert.True(t, docI == nil) assert.DeepEquals(t, doc2.body, Body{"bar": float64(-1)}) }
func TestShadowerPull(t *testing.T) { bucket := makeExternalBucket() defer bucket.Close() bucket.Set("key1", 0, Body{"foo": 1}) bucket.Set("key2", 0, Body{"bar": -1}) bucket.SetRaw("key3", 0, []byte("qwertyuiop")) //will be ignored db := setupTestDB(t) defer tearDownTestDB(t, db) shadower, err := NewShadower(db.DatabaseContext, bucket, nil) assertNoError(t, err, "NewShadower") defer shadower.Stop() base.Log("Waiting for shadower to catch up...") var doc1, doc2 *document waitFor(t, func() bool { seq, _ := db.LastSequence() return seq >= 2 }) doc1, _ = db.GetDoc("key1") doc2, _ = db.GetDoc("key2") assert.DeepEquals(t, doc1.body, Body{"foo": float64(1)}) assert.DeepEquals(t, doc2.body, Body{"bar": float64(-1)}) base.Log("Deleting remote doc") bucket.Delete("key1") waitFor(t, func() bool { seq, _ := db.LastSequence() return seq >= 3 }) doc1, _ = db.GetDoc("key1") assert.True(t, doc1.hasFlag(channels.Deleted)) _, err = db.Get("key1") assert.DeepEquals(t, err, &base.HTTPError{Status: 404, Message: "deleted"}) }
// ADMIN API to turn Go CPU profiling on/off func (h *handler) handleProfiling() error { profileName := h.PathVar("name") var params struct { File string `json:"file"` } body, err := h.readBody() if err != nil { return err } if len(body) > 0 { if err = json.Unmarshal(body, ¶ms); err != nil { return err } } if params.File != "" { f, err := os.Create(params.File) if err != nil { return err } if profileName != "" { defer f.Close() if profile := pprof.Lookup(profileName); profile != nil { profile.WriteTo(f, 0) base.Logf("Wrote %s profile to %s", profileName, params.File) } else { return base.HTTPErrorf(http.StatusNotFound, "No such profile %q", profileName) } } else { base.Logf("Starting CPU profile to %s ...", params.File) pprof.StartCPUProfile(f) } } else { if profileName != "" { return base.HTTPErrorf(http.StatusBadRequest, "Missing JSON 'file' parameter") } else { base.Log("...ending CPU profile.") pprof.StopCPUProfile() } } return nil }
func (sc *ServerContext) installPrincipals(context *db.DatabaseContext, spec map[string]*db.PrincipalConfig, what string) error { for name, princ := range spec { isGuest := name == base.GuestUsername if isGuest { internalName := "" princ.Name = &internalName } else { princ.Name = &name } _, err := context.UpdatePrincipal(*princ, (what == "user"), isGuest) if err != nil { // A conflict error just means updatePrincipal didn't overwrite an existing user. if status, _ := base.ErrorAsHTTPStatus(err); status != http.StatusConflict { return fmt.Errorf("Couldn't create %s %q: %v", what, name, err) } } else if isGuest { base.Log(" Reset guest user to config") } else { base.Logf(" Created %s %q", what, name) } } return nil }
// Re-runs the sync function on every current document in the database (if doCurrentDocs==true) // and/or imports docs in the bucket not known to the gateway (if doImportDocs==true). // To be used when the JavaScript sync function changes. func (db *Database) UpdateAllDocChannels(doCurrentDocs bool, doImportDocs bool) (int, error) { if doCurrentDocs { base.Log("Recomputing document channels...") } if doImportDocs { base.Log("Importing documents...") } else if !doCurrentDocs { return 0, nil // no-op if neither option is set } options := Body{"stale": false, "reduce": false} if !doCurrentDocs { options["endkey"] = []interface{}{true} options["endkey_inclusive"] = false } else if !doImportDocs { options["startkey"] = []interface{}{true} } vres, err := db.Bucket.View(DesignDocSyncHousekeeping, ViewImport, options) if err != nil { return 0, err } // We are about to alter documents without updating their sequence numbers, which would // really confuse the changeCache, so turn it off until we're done: db.changeCache.EnableChannelLogs(false) defer db.changeCache.EnableChannelLogs(true) db.changeCache.ClearLogs() base.Logf("Re-running sync function on all %d documents...", len(vres.Rows)) changeCount := 0 for _, row := range vres.Rows { rowKey := row.Key.([]interface{}) docid := rowKey[1].(string) key := realDocID(docid) //base.Log("\tupdating %q", docid) err := db.Bucket.Update(key, 0, func(currentValue []byte) ([]byte, error) { // Be careful: this block can be invoked multiple times if there are races! if currentValue == nil { return nil, couchbase.UpdateCancel // someone deleted it?! } doc, err := unmarshalDocument(docid, currentValue) if err != nil { return nil, err } imported := false if !doc.hasValidSyncData() { // This is a document not known to the sync gateway. Ignore or import it: if !doImportDocs { return nil, couchbase.UpdateCancel } imported = true if err = db.initializeSyncData(doc); err != nil { return nil, err } base.LogTo("CRUD", "\tImporting document %q --> rev %q", docid, doc.CurrentRev) } else { if !doCurrentDocs { return nil, couchbase.UpdateCancel } base.LogTo("CRUD", "\tRe-syncing document %q", docid) } // Run the sync fn over each current/leaf revision, in case there are conflicts: changed := 0 doc.History.forEachLeaf(func(rev *RevInfo) { body, _ := db.getRevFromDoc(doc, rev.ID, false) channels, access, roles, err := db.getChannelsAndAccess(doc, body, rev.ID) if err != nil { // Probably the validator rejected the doc base.Warn("Error calling sync() on doc %q: %v", docid, err) access = nil channels = nil } rev.Channels = channels if rev.ID == doc.CurrentRev { changed = len(doc.Access.updateAccess(doc, access)) + len(doc.RoleAccess.updateAccess(doc, roles)) + len(doc.updateChannels(channels)) } }) if changed > 0 || imported { base.LogTo("Access", "Saving updated channels and access grants of %q", docid) return json.Marshal(doc) } else { return nil, couchbase.UpdateCancel } }) if err == nil { changeCount++ } else if err != couchbase.UpdateCancel { base.Warn("Error updating doc %q: %v", docid, err) } } base.Logf("Finished re-running sync function; %d docs changed", changeCount) if changeCount > 0 { // Now invalidate channel cache of all users/roles: base.Log("Invalidating channel caches of users/roles...") users, roles, _ := db.AllPrincipalIDs() for _, name := range users { db.invalUserChannels(name) } for _, name := range roles { db.invalRoleChannels(name) } } return changeCount, nil }