Exemple #1
0
// Adds a new change to a channel log.
func (db *Database) AddToChangeLog(channelName string, entry channels.LogEntry, parentRevID string) error {
	if channelName == "*" && !EnableStarChannelLog {
		return nil
	}
	var fullUpdate bool
	var removedCount int
	fullUpdateAttempts := 0

	logDocID := channelLogDocID(channelName)
	err := db.Bucket.WriteUpdate(logDocID, 0, func(currentValue []byte) ([]byte, walrus.WriteOptions, error) {
		// (Be careful: this block can be invoked multiple times if there are races!)
		// Should I do a full update of the change log, removing older entries to limit its size?
		// This has to be done occasionaly, but it's slower than simply appending to it. This
		// test is a heuristic that seems to strike a good balance in practice:
		fullUpdate = AlwaysCompactChangeLog ||
			(len(currentValue) > 20000 && (rand.Intn(100) < len(currentValue)/5000))
		removedCount = 0

		if len(currentValue) == 0 {
			channelLog := channels.ChangeLog{}
			channelLog.Add(entry)
			return encodeChannelLog(&channelLog), walrus.Raw, nil
		}

		if fullUpdate {
			fullUpdateAttempts++
			var newValue bytes.Buffer
			removedCount = channels.TruncateEncodedChangeLog(bytes.NewReader(currentValue),
				MaxChangeLogLength-1, &newValue)
			if removedCount > 0 {
				entry.Encode(&newValue, parentRevID)
				return newValue.Bytes(), walrus.Raw, nil
			}
		}

		w := bytes.NewBuffer(make([]byte, 0, 50000))
		entry.Encode(w, parentRevID)
		currentValue = append(currentValue, w.Bytes()...)
		return currentValue, walrus.Raw, nil
	})

	/*if fullUpdate {
		base.Log("Removed %d entries from %q", removedCount, channelName)
	} else if fullUpdateAttempts > 0 {
		base.Log("Attempted to remove entries %d times but failed", fullUpdateAttempts)
	}*/
	return err
}
// Writes new changes to my channel log document.
func (c *channelLogWriter) addToChangeLog_(entries []*changeEntry) {
	var err error
	dbExpvars.Add("channelLogAdds", 1)
	logDocID := channelLogDocID(c.channelName)

	// A fraction of the time we will do a full update and clean stuff out.
	fullUpdate := AlwaysCompactChangeLog || len(entries) > MaxChangeLogLength/2 || rand.Intn(MaxChangeLogLength/len(entries)) == 0
	if !fullUpdate {
		// Non-full update; just append the new entries:
		w := bytes.NewBuffer(make([]byte, 0, 100*len(entries)))
		for _, entry := range entries {
			entry.logEntry.Encode(w, entry.parentRevID)
		}
		data := w.Bytes()
		err = c.bucket.Append(logDocID, data)
		if err == nil {
			base.LogTo("ChannelLog", "Appended %d sequence(s) to %q", len(entries), c.channelName)
			dbExpvars.Add("channelLogAppends", 1)
		} else if base.IsDocNotFoundError(err) {
			// Append failed due to doc not existing, so fall back to full update
			err = nil
			fullUpdate = true
		} else {
			base.Warn("Error appending to %q -- %v", len(entries), c.channelName, err)
		}
	}

	if fullUpdate {
		// Full update: do a CAS-based read+write:
		fullUpdateAttempts := 0
		var oldChangeLogCount, newChangeLogCount int
		err = c.bucket.WriteUpdate(logDocID, 0, func(currentValue []byte) ([]byte, walrus.WriteOptions, error) {
			fullUpdateAttempts++
			numToKeep := MaxChangeLogLength - len(entries)
			if len(currentValue) == 0 || numToKeep <= 0 {
				// If log was missing or empty, or will be entirely overwritten, create a new one:
				entriesToWrite := entries
				if numToKeep < 0 {
					entriesToWrite = entries[-numToKeep:]
				}
				channelLog := channels.ChangeLog{}
				for _, entry := range entriesToWrite {
					channelLog.Add(*entry.logEntry)
				}
				newChangeLogCount = len(entriesToWrite)
				oldChangeLogCount = newChangeLogCount
				return encodeChannelLog(&channelLog), walrus.Raw, nil
			} else {
				// Append to an already existing change log:
				var newValue bytes.Buffer
				var nRemoved int
				nRemoved, newChangeLogCount = channels.TruncateEncodedChangeLog(
					bytes.NewReader(currentValue), numToKeep, numToKeep/2, &newValue)
				for _, entry := range entries {
					entry.logEntry.Encode(&newValue, entry.parentRevID)
				}
				oldChangeLogCount = nRemoved + newChangeLogCount
				newChangeLogCount += len(entries)
				return newValue.Bytes(), walrus.Raw, nil
			}
		})
		if err == nil {
			dbExpvars.Add("channelLogRewrites", 1)
			dbExpvars.Add("channelLogRewriteCollisions", int64(fullUpdateAttempts-1))
			base.LogTo("ChannelLog", "Wrote %d sequences (was %d now %d) to %q in %d attempts",
				len(entries), oldChangeLogCount, newChangeLogCount, c.channelName, fullUpdateAttempts)
		} else {
			base.Warn("Error writing %d sequence(s) to %q -- %v", len(entries), c.channelName, err)
		}
	}
}