go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/appengine/coordinator/logStreamState.go (about) 1 // Copyright 2015 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 coordinator 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "time" 22 23 ds "go.chromium.org/luci/gae/service/datastore" 24 "go.chromium.org/luci/logdog/common/types" 25 ) 26 27 // ArchivalState describes the archival state of a LogStream. 28 type ArchivalState int 29 30 const ( 31 // NotArchived means that the stream is not archived, and that no archival has 32 // been tasked. 33 NotArchived ArchivalState = iota 34 // ArchiveTasked is true if the log stream has an archival tasked, but has 35 // not yet been archived. 36 ArchiveTasked 37 // ArchivedPartial means that the stream is archived, but that some log 38 // entries are missing. 39 ArchivedPartial 40 // ArchivedComplete means that the stream is archived and all log entries are 41 // present. 42 ArchivedComplete 43 ) 44 45 // Archived returns true if this ArchivalState implies that the log stream is 46 // archived. 47 func (as ArchivalState) Archived() bool { 48 switch as { 49 case ArchivedPartial, ArchivedComplete: 50 return true 51 default: 52 return false 53 } 54 } 55 56 // ArchivalStateKey is the name of the index key for the archival state. 57 var ArchivalStateKey = "_ArchivalState" 58 59 // LogStreamStateExpiry is the duration after creation that a LogStreamState 60 // record should persist for. After this duration it may be deleted. 61 const LogStreamStateExpiry = 540 * 24 * time.Hour 62 63 // LogStreamState contains the current state of a LogStream. 64 // 65 // This structure has additional datastore fields imposed by the 66 // PropertyLoadSaver. 67 // - _Terminated is true if the LogStream has been terminated. 68 // - _ArchivePending is true if the LogStream currently has an archive task 69 // dispatched. 70 // - _ArchivalState is true if the LogStream has been archived. 71 // 72 // See services API's LogStreamState message type. 73 type LogStreamState struct { 74 _id int `gae:"$id,1"` 75 // Parent is the key of the corresponding LogStream. 76 Parent *ds.Key `gae:"$parent"` 77 78 // Schema is the datastore schema version for this object. This can be used 79 // to facilitate schema migrations. 80 // 81 // The current schema is CurrentSchemaVersion. 82 Schema string `gae:",noindex"` 83 84 // Created is the last time that this state has been created. 85 Created time.Time `gae:",noindex"` 86 // Updated is the last time that this state has been updated. 87 Updated time.Time `gae:",noindex"` 88 // ExpireAt is time after which the state will be deleted. 89 ExpireAt time.Time `gae:",noindex"` 90 91 // Secret is the Butler secret value for this stream. 92 // 93 // This value may only be returned to LogDog services; it is not user-visible. 94 Secret []byte `gae:",noindex"` 95 96 // TerminatedTime is the Coordinator's record of when this log stream was 97 // terminated. 98 TerminatedTime time.Time `gae:",noindex"` 99 // TerminalIndex is the index of the last log entry in the stream. 100 // 101 // If this is <0, the log stream is either still streaming or has been 102 // archived with no log entries. 103 TerminalIndex int64 `gae:",noindex"` 104 105 // ArchiveRetryCount is the number of times this stream has attempted 106 // archival. 107 ArchiveRetryCount int64 108 109 // ArchivedTime is the Coordinator's record of when this log stream was 110 // archived. If this is non-zero, it means that the log entry has been 111 // archived. 112 ArchivedTime time.Time `gae:",noindex"` 113 // ArchiveLogEntryCount is the number of LogEntry records that were archived 114 // for this log stream. 115 // 116 // This is valid only if the log stream is Archived. 117 ArchiveLogEntryCount int64 `gae:",noindex"` 118 // ArchivalKey is the archival key for this log stream. This is used to 119 // differentiate the real archival request from those that were dispatched, 120 // but that ultimately failed to update state. 121 // 122 // See createArchivalKey for details on its generation and usage. 123 ArchivalKey []byte `gae:",noindex"` 124 125 // ArchiveIndexURL is the Google Storage URL where the log stream's index is 126 // archived. 127 ArchiveIndexURL string `gae:",noindex"` 128 // ArchiveIndexSize is the size, in bytes, of the archived Index. It will be 129 // zero if the file is not archived. 130 ArchiveIndexSize int64 `gae:",noindex"` 131 // ArchiveStreamURL is the Google Storage URL where the log stream's raw 132 // stream data is archived. If this is not empty, the log stream is considered 133 // archived. 134 ArchiveStreamURL string `gae:",noindex"` 135 // ArchiveStreamSize is the size, in bytes, of the archived stream. It will be 136 // zero if the file is not archived. 137 ArchiveStreamSize int64 `gae:",noindex"` 138 139 // extra causes datastore to ignore unrecognized fields and strip them in 140 // future writes. 141 extra ds.PropertyMap `gae:"-,extra"` 142 } 143 144 // NewLogStreamState returns a LogStreamState with its parent key populated to 145 // the LogStream with the supplied ID. 146 func NewLogStreamState(c context.Context, id HashID) *LogStreamState { 147 return &LogStreamState{Parent: ds.NewKey(c, "LogStream", string(id), 0, nil)} 148 } 149 150 // ID returns the LogStream ID for the LogStream that owns this LogStreamState. 151 func (lst *LogStreamState) ID() HashID { 152 return HashID(lst.Parent.StringID()) 153 } 154 155 // Load implements ds.PropertyLoadSaver. 156 func (lst *LogStreamState) Load(pmap ds.PropertyMap) error { 157 // Discard derived properties. 158 delete(pmap, "_Terminated") 159 delete(pmap, ArchivalStateKey) 160 161 return ds.GetPLS(lst).Load(pmap) 162 } 163 164 // Save implements ds.PropertyLoadSaver. 165 func (lst *LogStreamState) Save(withMeta bool) (ds.PropertyMap, error) { 166 lst.Schema = CurrentSchemaVersion 167 168 // Save default struct fields. 169 pmap, err := ds.GetPLS(lst).Save(withMeta) 170 if err != nil { 171 return nil, err 172 } 173 174 pmap["_Terminated"] = ds.MkProperty(lst.Terminated()) 175 pmap[ArchivalStateKey] = ds.MkProperty(lst.ArchivalState()) 176 177 return pmap, nil 178 } 179 180 // Validate evaluates the state and data contents of the LogStreamState and 181 // returns an error if it is invalid. 182 func (lst *LogStreamState) Validate() error { 183 if lst.Created.IsZero() { 184 return errors.New("missing created time") 185 } 186 if lst.Updated.IsZero() { 187 return errors.New("missing updated time") 188 } 189 190 if lst.Terminated() && lst.TerminatedTime.IsZero() { 191 return errors.New("log stream is terminated, but missing terminated time") 192 } 193 if err := types.PrefixSecret(lst.Secret).Validate(); err != nil { 194 return fmt.Errorf("invalid prefix secret: %v", err) 195 } 196 197 return nil 198 } 199 200 // Terminated returns true if this stream has been terminated. 201 func (lst *LogStreamState) Terminated() bool { 202 if lst.ArchivalState().Archived() { 203 return true 204 } 205 return lst.TerminalIndex >= 0 206 } 207 208 // ArchivalState returns the archival state of the log stream. 209 func (lst *LogStreamState) ArchivalState() ArchivalState { 210 if lst.ArchivedTime.IsZero() { 211 // Not archived, have we dispatched an archival? 212 if len(lst.ArchivalKey) > 0 { 213 return ArchiveTasked 214 } 215 return NotArchived 216 } 217 218 if lst.ArchiveLogEntryCount > lst.TerminalIndex { 219 return ArchivedComplete 220 } 221 return ArchivedPartial 222 }