code: func processShard(c context.Context, cfg *Config, timestamp time.Time, shard uint64, loop bool) error {
  
  	logging.Fields{
  		"shard": shard,
  	}.Infof(c, "Processing tumble shard.")
  
  	q := processShardQuery(c, cfg, shard)
  	if q == nil {
  		logging.Warningf(c, "dead shard, quitting")
  		return nil
  	}
  
  	// Calculate our end itme. If we're not looping or we have a <= 0 duration,
  	// we will perform a single loop.
  	var endTime time.Time
  	if cfg.ProcessLoopDuration > 0 {
  		endTime = clock.Now(c).Add(time.Duration(cfg.ProcessLoopDuration))
  		logging.Debugf(c, "Process loop is configured to exit after [%s] at %s",
  			cfg.ProcessLoopDuration.String(), endTime)
  	}
  
  	// Lock around the shard that we are trying to modify.
  	//
  	// Since memcache is namespaced, we don't need to include the namespace in our
  	// lock name.
  	task := makeProcessTask(timestamp, endTime, shard, loop)
  	lockKey := fmt.Sprintf("%s.%d.lock", baseName, shard)
  	clientID := fmt.Sprintf("%d_%d_%s", timestamp.Unix(), shard, info.RequestID(c))
  
  	err := memlock.TryWithLock(c, lockKey, clientID, func(c context.Context) error {
  		return task.process(c, cfg, q)
  	})
  	if err == memlock.ErrFailedToLock {
  		logging.Infof(c, "Couldn't obtain lock (giving up): %s", err)
  		return nil
  	}
  	return err
  }
docs: // processShard is the tumble backend endpoint. This accepts a shard number
  // which is expected to be < GlobalConfig.NumShards.