Ejemplo n.º 1
0
func (s *sequenceHasher) loadClocks(hashValue uint64) (*storedClocks, error) {

	stored := storedClocks{}
	key := kHashPrefix + strconv.FormatUint(hashValue, 10)

	bytes, cas, err := s.bucket.GetAndTouchRaw(key, base.SecondsToCbsExpiry(int(s.hashExpiry)))
	IndexExpvars.Add("get_hashLoadClocks", 1)

	if err != nil {
		// Assume no clocks stored for this string
		return &stored, nil
	}
	if err = stored.Unmarshal(bytes); err != nil {
		base.Warn("Error unmarshalling stored clocks for key [%s], returning zero sequence", key)
		return &stored, errors.New("Error unmarshalling stored clocks for key")
	}
	stored.cas = cas
	return &stored, nil
}
Ejemplo n.º 2
0
func (s *sequenceHasher) GetHash(clock base.SequenceClock) (string, error) {

	if clock == nil {
		return "", errors.New("Can't calculate hash for nil clock")
	}

	hashValue := s.calculateHash(clock)

	// Load stored clocks for this hash, to see if it's already been defined.
	// Note: getCacheValue and load are handled as separate operations to optimize locking.
	//   1. getCacheValue locks the cache, and retrieves the current cache entry (or creates a new empty entry if not found)
	//   2. cachedValue.load locks the entry, and loads from the DB if no previous entry is found
	cachedValue := s.getCacheValue(hashValue)
	cachedClocks, err := cachedValue.load(s.loadClocks)
	if err != nil {
		return "", err
	}

	// Check whether the cached clocks for the hash value match our clock
	exists, index := cachedClocks.Contains(clock.Value())
	if exists {
		seqHash := sequenceHash{
			hashValue:      hashValue,
			collisionIndex: uint16(index),
		}
		IndexExpvars.Add("seqHash_getHash_hits", 1)
		return seqHash.String(), nil
	}

	// Didn't find a match in cache - update the index and the cache.  Get a write lock on the index value
	// first, to ensure only one goroutine on this SG attempts to write.  writeCas handling below handles
	// the case where other SGs are updating the value concurrently
	IndexExpvars.Add("seqHash_getHash_misses", 1)

	// First copy the clock value, to ensure we store a non-mutable version in the cache
	clockValue := make([]uint64, len(clock.Value()))
	copy(clockValue, clock.Value())

	updateErr := func() error {
		cachedValue.lock.Lock()
		defer cachedValue.lock.Unlock()

		// If the number of cached clocks has changed, check whether someone else has added this clock
		// while we waited for the lock
		if len(cachedValue.clocks.Sequences) > len(cachedClocks.Sequences) {
			exists, index = cachedValue.clocks.Contains(clockValue)
			if exists {
				return nil
			}
		}

		// Add our clock to the cached clocks for this hash
		existingClocks := cachedValue.clocks
		existingClocks.Sequences = append(existingClocks.Sequences, clockValue)

		// Update the hash entry in the bucket
		key := kHashPrefix + strconv.FormatUint(hashValue, 10)
		initialValue, err := existingClocks.Marshal()
		index = len(existingClocks.Sequences) - 1
		if err != nil {
			return err
		}
		_, err = base.WriteCasRaw(s.bucket, key, initialValue, existingClocks.cas, base.SecondsToCbsExpiry(int(s.hashExpiry)), func(value []byte) (updatedValue []byte, err error) {
			// Note: The following is invoked upon cas failure - may be called multiple times
			base.LogTo("DIndex+", "CAS fail - reapplying changes for hash storage for key: %s", key)
			var sClocks storedClocks
			err = sClocks.Unmarshal(value)
			if err != nil {
				base.Warn("Error unmarshalling hash storage during update", err)
				return nil, err
			}
			exists, index = sClocks.Contains(clockValue)
			if exists {
				// return empty byte array to cancel the update
				return []byte{}, nil
			}
			// Not found - add
			sClocks.Sequences = append(sClocks.Sequences, clockValue)
			base.LogTo("DIndex+", "Reattempting stored hash write for key %s:", key)
			index = len(sClocks.Sequences) - 1
			return sClocks.Marshal()
		})
		return nil
	}()

	if updateErr != nil {
		return "", updateErr
	}

	IndexExpvars.Add("writeCasRaw_hash", 1)

	if err != nil && err.Error() != "Already Exists" {
		return "", err
	}

	seqHash := &sequenceHash{
		hashValue:      hashValue,
		collisionIndex: uint16(index),
	}
	return seqHash.String(), nil
}