Acquire will load the fs.FS for the given file. It guarantees that parallel calls for the same fileID will only result in one fetch, and that parallel calls for distinct fileIDs will fetch in parallel. Safety: Every call to Acquire that does not return an error must call close on the returned value
(ctx context.Context, db database.Store, fileID uuid.UUID)
| 146 | // Safety: Every call to Acquire that does not return an error must call close |
| 147 | // on the returned value when it is done being used. |
| 148 | func (c *Cache) Acquire(ctx context.Context, db database.Store, fileID uuid.UUID) (*CloseFS, error) { |
| 149 | // It's important that this `Load` call occurs outside `prepare`, after the |
| 150 | // mutex has been released, or we would continue to hold the lock until the |
| 151 | // entire file has been fetched, which may be slow, and would prevent other |
| 152 | // files from being fetched in parallel. |
| 153 | e := c.prepare(db, fileID) |
| 154 | ev, err := e.value.Load() |
| 155 | if err != nil { |
| 156 | c.lock.Lock() |
| 157 | defer c.lock.Unlock() |
| 158 | e.close() |
| 159 | e.purge() |
| 160 | return nil, err |
| 161 | } |
| 162 | |
| 163 | cleanup := func() { |
| 164 | c.lock.Lock() |
| 165 | defer c.lock.Unlock() |
| 166 | e.close() |
| 167 | } |
| 168 | |
| 169 | // We always run the fetch under a system context and actor, so we need to |
| 170 | // check the caller's context (including the actor) manually before returning. |
| 171 | |
| 172 | // Check if the caller's context was canceled. Even though `Authorize` takes |
| 173 | // a context, we still check it manually first because none of our mock |
| 174 | // database implementations check for context cancellation. |
| 175 | if err := ctx.Err(); err != nil { |
| 176 | cleanup() |
| 177 | return nil, err |
| 178 | } |
| 179 | |
| 180 | // Check that the caller is authorized to access the file |
| 181 | subject, ok := dbauthz.ActorFromContext(ctx) |
| 182 | if !ok { |
| 183 | cleanup() |
| 184 | return nil, dbauthz.ErrNoActor |
| 185 | } |
| 186 | if err := c.authz.Authorize(ctx, subject, policy.ActionRead, ev.Object); err != nil { |
| 187 | cleanup() |
| 188 | return nil, err |
| 189 | } |
| 190 | |
| 191 | var closeOnce sync.Once |
| 192 | return &CloseFS{ |
| 193 | FS: ev.FS, |
| 194 | close: func() { |
| 195 | // sync.Once makes the Close() idempotent, so we can call it |
| 196 | // multiple times without worrying about double-releasing. |
| 197 | closeOnce.Do(func() { |
| 198 | c.lock.Lock() |
| 199 | defer c.lock.Unlock() |
| 200 | e.close() |
| 201 | }) |
| 202 | }, |
| 203 | }, nil |
| 204 | } |
| 205 |