go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/deploy/service/model/expiry.go (about) 1 // Copyright 2022 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 model 16 17 import ( 18 "context" 19 "time" 20 21 "go.chromium.org/luci/common/clock" 22 "go.chromium.org/luci/common/errors" 23 "go.chromium.org/luci/common/logging" 24 "go.chromium.org/luci/common/retry/transient" 25 "go.chromium.org/luci/common/sync/parallel" 26 "go.chromium.org/luci/gae/service/datastore" 27 28 "go.chromium.org/luci/deploy/api/modelpb" 29 ) 30 31 // ExpireActuations moves expired actuations into EXPIRED state. 32 func ExpireActuations(ctx context.Context) error { 33 ctx, done := clock.WithTimeout(ctx, time.Minute) 34 defer done() 35 36 q := datastore.NewQuery("Actuation"). 37 Eq("State", modelpb.Actuation_EXECUTING). 38 Lt("Expiry", clock.Now(ctx).UTC()). 39 KeysOnly(true) 40 41 err := parallel.WorkPool(16, func(work chan<- func() error) { 42 err := datastore.Run(ctx, q, func(key *datastore.Key) { 43 work <- func() error { 44 ctx := logging.SetField(ctx, "actuation", key.StringID()) 45 logging.Infof(ctx, "Expiring") 46 if err := expireActuation(ctx, key.StringID()); err != nil { 47 logging.Errorf(ctx, "Failed: %s", err) 48 } 49 return nil 50 } 51 }) 52 if err != nil { 53 logging.Errorf(ctx, "Datastore query failed: %s", err) 54 work <- func() error { return err } 55 } 56 }) 57 return transient.Tag.Apply(err) 58 } 59 60 func expireActuation(ctx context.Context, actuationID string) error { 61 return Txn(ctx, func(ctx context.Context) error { 62 a := Actuation{ID: actuationID} 63 if err := datastore.Get(ctx, &a); err != nil { 64 return errors.Annotate(err, "fetching Actuation entity").Err() 65 } 66 if a.State == modelpb.Actuation_EXECUTING { 67 op, err := NewActuationEndOp(ctx, &a) 68 if err != nil { 69 return errors.Annotate(err, "starting Actuation end op").Err() 70 } 71 op.Expire(ctx) 72 if err := op.Apply(ctx); err != nil { 73 return errors.Annotate(err, "applying changes").Err() 74 } 75 } 76 return nil 77 }) 78 }