OLD | NEW |
1 package model | 1 package model |
2 | 2 |
3 import ( | 3 import ( |
4 "bytes" | 4 "bytes" |
5 "fmt" | 5 "fmt" |
6 "io" | 6 "io" |
7 "io/ioutil" | 7 "io/ioutil" |
8 "math" | 8 "math" |
9 "time" | 9 "time" |
10 | 10 |
11 "golang.org/x/net/context" | 11 "golang.org/x/net/context" |
12 | 12 |
13 "github.com/luci/gae/service/datastore" | 13 "github.com/luci/gae/service/datastore" |
14 "github.com/luci/luci-go/common/errors" | 14 "github.com/luci/luci-go/common/errors" |
15 "github.com/luci/luci-go/common/logging" | 15 "github.com/luci/luci-go/common/logging" |
16 ) | 16 ) |
17 | 17 |
18 // IsAggregateTestFile returns whether filename is that of an aggregate test fil
e. | 18 // IsAggregateTestFile returns whether filename is that of an aggregate test fil
e. |
19 func IsAggregateTestFile(filename string) bool { | 19 func IsAggregateTestFile(filename string) bool { |
20 switch filename { | 20 switch filename { |
21 case "results.json", "results-small.json": | 21 case "results.json", "results-small.json": |
22 return true | 22 return true |
23 default: | 23 default: |
24 return false | 24 return false |
25 } | 25 } |
26 } | 26 } |
27 | 27 |
28 // BuildNum is int64 that is used to handle TestFile datastore records | 28 // BuildNum is int64 that is used to handle TestFile datastore records |
29 // with null build_number. The value is >= 0 if the datastore value | 29 // with null build_number. The value is >= 0 if the datastore value |
30 // was not null. | 30 // was not null. The value is -1 for null datastore value. |
31 type BuildNum int64 | 31 type BuildNum int64 |
32 | 32 |
33 var _ datastore.PropertyConverter = (*BuildNum)(nil) | 33 var _ datastore.PropertyConverter = (*BuildNum)(nil) |
34 | 34 |
35 // IsNil returns whether b had null value in datastore. | 35 // IsNil returns whether b had null value in datastore. |
36 func (b *BuildNum) IsNil() bool { return *b == -1 } | 36 func (b *BuildNum) IsNil() bool { return *b == -1 } |
37 | 37 |
38 // ToProperty is for implementing datastore.PropertyConverter. | 38 // ToProperty is for implementing datastore.PropertyConverter. |
39 func (b *BuildNum) ToProperty() (p datastore.Property, err error) { | 39 func (b *BuildNum) ToProperty() (p datastore.Property, err error) { |
40 if b.IsNil() { | 40 if b.IsNil() { |
(...skipping 17 matching lines...) Expand all Loading... |
58 } | 58 } |
59 | 59 |
60 // DataEntry represents a DataEntry record. | 60 // DataEntry represents a DataEntry record. |
61 type DataEntry struct { | 61 type DataEntry struct { |
62 ID int64 `gae:"$id"` | 62 ID int64 `gae:"$id"` |
63 Data []byte `gae:"data,noindex"` | 63 Data []byte `gae:"data,noindex"` |
64 } | 64 } |
65 | 65 |
66 // TestFile represents a TestFile record. | 66 // TestFile represents a TestFile record. |
67 type TestFile struct { | 67 type TestFile struct { |
68 » ID int64 `gae:"$id"` | 68 » ID int64 `gae:"$id"` |
69 » BuildNumber BuildNum `gae:"build_number"` | 69 » BuildNumber BuildNum `gae:"build_number"` |
70 » Builder string `gae:"builder"` | 70 » Builder string `gae:"builder"` |
71 » DataKeys []*datastore.Key `gae:"data_keys,noindex"` | 71 » Master string `gae:"master"` |
72 » LastMod time.Time `gae:"date"` | 72 » Name string `gae:"name"` |
73 » Master string `gae:"master"` | 73 » TestType string `gae:"test_type"` |
74 » Name string `gae:"name"` | 74 |
75 » TestType string `gae:"test_type"` | 75 » // DataKeys is the keys to the DataEntry(s) that contain |
| 76 » // the data for this TestFile. |
| 77 » DataKeys []*datastore.Key `gae:"data_keys,noindex"` |
| 78 |
| 79 » // LastMod is the last modified time. |
| 80 » LastMod time.Time `gae:"date"` |
76 | 81 |
77 // Data is the data in the DataEntry(s) pointed to by DataKeys. | 82 // Data is the data in the DataEntry(s) pointed to by DataKeys. |
78 // After loading a TestFile from the datastore, this field is | 83 // After loading a TestFile from the datastore, this field is |
79 // only available after GetData is called. To put updated | 84 // only available after GetData is called. To put updated |
80 // data in this field to the datastore, call PutData. | 85 // data in this field to the datastore, call PutData. |
81 // | 86 // |
82 » // Users should typically perform the following sequence of calls | 87 » // Users will typically perform the following sequence of calls |
83 // in a transaction: | 88 // in a transaction: |
84 // | 89 // |
85 » // - datastore.Get(tf) | 90 » // ds = datastore.Get(ctx) |
86 » // - tf.GetData() | 91 » // err = ds.Get(tf) |
87 » // - tf.PutData() | 92 » // err = tf.GetData(ctx) |
88 » // - datastore.Put(tf) | 93 » // // manipulate tf.Data |
89 » // - datastore.Delete(tf.OldDataKeys) | 94 » // err = tf.PutData(ctx) |
| 95 » // err = ds.Put(tf) |
| 96 » // err = ds.Delete(tf.OldDataKeys) |
90 // | 97 // |
91 Data io.Reader `gae:"-,noindex"` | 98 Data io.Reader `gae:"-,noindex"` |
92 | 99 |
93 // OldDataKeys is the keys of the old DataEntry(s) used | 100 // OldDataKeys is the keys of the old DataEntry(s) used |
94 // before putting the new data. This field is set after a | 101 // before putting the new data. This field is set after a |
95 // successful call to PutData. | 102 // successful call to PutData. |
96 // | 103 // |
97 // Users are responsible for deleting the DataEntry(s) | 104 // Users are responsible for deleting the DataEntry(s) |
98 // pointed to by these keys if they are no longer needed. | 105 // pointed to by these keys if they are no longer needed. |
99 OldDataKeys []*datastore.Key `gae:"-,noindex"` | 106 OldDataKeys []*datastore.Key `gae:"-,noindex"` |
100 | 107 |
101 » // newDataKeys is unused in this implementation. It is | 108 » // NewDataKeys is UNUSED in this implementation. It is |
102 // a remnant of the old Python implementation. | 109 // a remnant of the old Python implementation. |
103 » newDataKeys []*datastore.Key `gae:"new_data_keys,noindex"` | 110 » NewDataKeys []*datastore.Key `gae:"new_data_keys,noindex"` |
104 } | 111 } |
105 | 112 |
106 // GetData fetches data from the DataEntry(s) pointed to by tf.DataKeys | 113 // GetData fetches data from the DataEntry(s) pointed to by tf.DataKeys |
107 // and sets tf.Data to the fetched data. | 114 // and sets tf.Data to the fetched data. |
108 func (tf *TestFile) GetData(c context.Context) error { | 115 func (tf *TestFile) GetData(c context.Context) error { |
109 ids := make([]int64, len(tf.DataKeys)) | 116 ids := make([]int64, len(tf.DataKeys)) |
110 for i, dk := range tf.DataKeys { | 117 for i, dk := range tf.DataKeys { |
111 ids[i] = dk.IntID() // Intentional: ignores the Kind stored in d
k. | 118 ids[i] = dk.IntID() // Intentional: ignores the Kind stored in d
k. |
112 } | 119 } |
113 ids, err := updatedBlobKeys(c, ids...) | 120 ids, err := updatedBlobKeys(c, ids...) |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
160 // tf.Data and updates tf.LastMod and tf.DataKeys locally. | 167 // tf.Data and updates tf.LastMod and tf.DataKeys locally. |
161 // If the returned error is non-nil, tf will be unmodified, | 168 // If the returned error is non-nil, tf will be unmodified, |
162 // except that tf.Data may have been consumed. | 169 // except that tf.Data may have been consumed. |
163 func (tf *TestFile) putDataEntries(c context.Context) error { | 170 func (tf *TestFile) putDataEntries(c context.Context) error { |
164 // Maximum data entries in a TestFile. | 171 // Maximum data entries in a TestFile. |
165 const maxDataEntries = 30 | 172 const maxDataEntries = 30 |
166 // 1 megabyte is the maximum allowed blob length. | 173 // 1 megabyte is the maximum allowed blob length. |
167 // See https://code.googlesource.com/gocloud/+/master/datastore/prop.go#
29. | 174 // See https://code.googlesource.com/gocloud/+/master/datastore/prop.go#
29. |
168 const maxBlobLen = 1 << 20 | 175 const maxBlobLen = 1 << 20 |
169 | 176 |
170 » // TODO: Read maxBlobLen bytes at a time. See io.LimitedReader. | 177 » // TODO(maybe): Read maxBlobLen bytes at a time. See io.LimitedReader. |
171 | 178 |
172 data, err := ioutil.ReadAll(tf.Data) | 179 data, err := ioutil.ReadAll(tf.Data) |
173 if err != nil { | 180 if err != nil { |
174 return err | 181 return err |
175 } | 182 } |
176 | 183 |
177 if len(data) > maxDataEntries*maxBlobLen { | 184 if len(data) > maxDataEntries*maxBlobLen { |
178 return fmt.Errorf( | 185 return fmt.Errorf( |
179 "model: data too large %d bytes (max allowed %d bytes)", | 186 "model: data too large %d bytes (max allowed %d bytes)", |
180 len(data), | 187 len(data), |
(...skipping 12 matching lines...) Expand all Loading... |
193 if err := datastore.Get(c).Put(dataEntries); err != nil { | 200 if err := datastore.Get(c).Put(dataEntries); err != nil { |
194 return err | 201 return err |
195 } | 202 } |
196 | 203 |
197 newKeys := make([]*datastore.Key, 0, len(dataEntries)) | 204 newKeys := make([]*datastore.Key, 0, len(dataEntries)) |
198 for _, de := range dataEntries { | 205 for _, de := range dataEntries { |
199 newKeys = append(newKeys, datastore.Get(c).KeyForObj(&de)) | 206 newKeys = append(newKeys, datastore.Get(c).KeyForObj(&de)) |
200 } | 207 } |
201 | 208 |
202 tf.DataKeys = newKeys | 209 tf.DataKeys = newKeys |
203 » tf.LastMod = time.Now() | 210 » tf.LastMod = time.Now().UTC() |
204 return nil | 211 return nil |
205 } | 212 } |
206 | 213 |
207 // updatedBlobKeys fetches the updated blob keys for the old keys from their blo
bstore migration | 214 // updatedBlobKeys fetches the updated blob keys for the old keys from their blo
bstore migration |
208 // records. len(n) will equal len(old) if there is no error. If len(old)>1 and t
here is an error, | 215 // records. len(n) will equal len(old) if there is no error. If len(old)>1 and t
here is an error, |
209 // err will be of type MultiError and the error at index i will correspond to th
e | 216 // err will be of type MultiError and the error at index i will correspond to th
e |
210 // error in retrieving the updated blob key for the key at index i in old. A | 217 // error in retrieving the updated blob key for the key at index i in old. A |
211 // nil error indicates that all values returned in n are valid. | 218 // nil error indicates that all values returned in n are valid. |
212 func updatedBlobKeys(c context.Context, old ...int64) (n []int64, err error) { | 219 func updatedBlobKeys(c context.Context, old ...int64) (n []int64, err error) { |
213 ds := datastore.Get(c) | 220 ds := datastore.Get(c) |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
317 q = q.Order("-date") | 324 q = q.Order("-date") |
318 | 325 |
319 if p.Limit < 0 || p.Limit > defaultLimit { | 326 if p.Limit < 0 || p.Limit > defaultLimit { |
320 q = q.Limit(defaultLimit) | 327 q = q.Limit(defaultLimit) |
321 } else { | 328 } else { |
322 q = q.Limit(p.Limit) | 329 q = q.Limit(p.Limit) |
323 } | 330 } |
324 | 331 |
325 return q | 332 return q |
326 } | 333 } |
OLD | NEW |