go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/common/lease/cl.go (about)

     1  // Copyright 2021 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package lease
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"time"
    21  
    22  	"go.chromium.org/luci/common/clock"
    23  	"go.chromium.org/luci/common/errors"
    24  
    25  	"go.chromium.org/luci/cv/internal/common"
    26  )
    27  
    28  // MakeCLResourceID returns ResourceID of a CL in CV.
    29  func MakeCLResourceID(clid common.CLID) ResourceID {
    30  	return ResourceID(fmt.Sprintf("CL/%d", clid))
    31  }
    32  
    33  // ApplyOnCL applies for an exclusive lease on the CL.
    34  //
    35  // Typically used before a Gerrit CL is mutated.
    36  //
    37  // Returns:
    38  //   - new time-limited context,
    39  //   - best-effort lease cancellation func,
    40  //   - and an error, if any.
    41  func ApplyOnCL(ctx context.Context, clid common.CLID, duration time.Duration, requester string) (context.Context, func(), error) {
    42  	leaseExpireTime := clock.Now(ctx).Add(duration)
    43  	if d, ok := ctx.Deadline(); ok && d.Before(leaseExpireTime) {
    44  		leaseExpireTime = d // Honor the deadline imposed by context
    45  	}
    46  	leaseExpireTime = leaseExpireTime.UTC().Truncate(time.Millisecond)
    47  	l, err := Apply(ctx, Application{
    48  		ResourceID: MakeCLResourceID(clid),
    49  		Holder:     requester,
    50  		ExpireTime: leaseExpireTime,
    51  	})
    52  	if err != nil {
    53  		return nil, nil, err
    54  	}
    55  
    56  	dctx, cancel := clock.WithDeadline(ctx, leaseExpireTime)
    57  	terminate := func() {
    58  		cancel()
    59  		if err := l.Terminate(ctx); err != nil {
    60  			// Best-effort termination since lease will expire naturally.
    61  			common.LogError(ctx, errors.Annotate(err, "failed to terminate a lease on the CL %d", clid).Err())
    62  		}
    63  	}
    64  	return dctx, terminate, nil
    65  }