github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/ref/manager.go (about) 1 package ref 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "strings" 8 "time" 9 10 "github.com/hashicorp/go-multierror" 11 "github.com/treeverse/lakefs/pkg/batch" 12 "github.com/treeverse/lakefs/pkg/cache" 13 "github.com/treeverse/lakefs/pkg/graveler" 14 "github.com/treeverse/lakefs/pkg/ident" 15 "github.com/treeverse/lakefs/pkg/kv" 16 "github.com/treeverse/lakefs/pkg/logging" 17 ) 18 19 const ( 20 // commitIDStringLength string representation length of commit ID - based on hex representation of sha256 21 commitIDStringLength = 64 22 // ImportExpiryTime Expiry time to remove imports from ref-store 23 ImportExpiryTime = 24 * time.Hour 24 ) 25 26 type CacheConfig struct { 27 Size int 28 Expiry time.Duration 29 Jitter time.Duration 30 } 31 32 type Manager struct { 33 kvStore kv.Store 34 kvStoreLimited kv.Store 35 addressProvider ident.AddressProvider 36 batchExecutor batch.Batcher 37 repoCache cache.Cache 38 commitCache cache.Cache 39 maxBatchDelay time.Duration 40 } 41 42 func branchFromProto(pb *graveler.BranchData) *graveler.Branch { 43 var sealedTokens []graveler.StagingToken 44 for _, st := range pb.SealedTokens { 45 sealedTokens = append(sealedTokens, graveler.StagingToken(st)) 46 } 47 branch := &graveler.Branch{ 48 CommitID: graveler.CommitID(pb.CommitId), 49 StagingToken: graveler.StagingToken(pb.StagingToken), 50 SealedTokens: sealedTokens, 51 } 52 return branch 53 } 54 55 func protoFromBranch(branchID graveler.BranchID, b *graveler.Branch) *graveler.BranchData { 56 var sealedTokens []string 57 for _, st := range b.SealedTokens { 58 sealedTokens = append(sealedTokens, st.String()) 59 } 60 branch := &graveler.BranchData{ 61 Id: branchID.String(), 62 CommitId: b.CommitID.String(), 63 StagingToken: b.StagingToken.String(), 64 SealedTokens: sealedTokens, 65 } 66 return branch 67 } 68 69 type ManagerConfig struct { 70 Executor batch.Batcher 71 KVStore kv.Store 72 KVStoreLimited kv.Store 73 AddressProvider ident.AddressProvider 74 RepositoryCacheConfig CacheConfig 75 CommitCacheConfig CacheConfig 76 MaxBatchDelay time.Duration 77 } 78 79 func NewRefManager(cfg ManagerConfig) *Manager { 80 return &Manager{ 81 kvStore: cfg.KVStore, 82 kvStoreLimited: cfg.KVStoreLimited, 83 addressProvider: cfg.AddressProvider, 84 batchExecutor: cfg.Executor, 85 repoCache: newCache(cfg.RepositoryCacheConfig), 86 commitCache: newCache(cfg.CommitCacheConfig), 87 maxBatchDelay: cfg.MaxBatchDelay, 88 } 89 } 90 91 func (m *Manager) getRepository(ctx context.Context, repositoryID graveler.RepositoryID) (*graveler.RepositoryRecord, error) { 92 data := graveler.RepositoryData{} 93 _, err := kv.GetMsg(ctx, m.kvStore, graveler.RepositoriesPartition(), []byte(graveler.RepoPath(repositoryID)), &data) 94 if err != nil { 95 if errors.Is(err, kv.ErrNotFound) { 96 err = graveler.ErrRepositoryNotFound 97 } 98 return nil, err 99 } 100 return graveler.RepoFromProto(&data), nil 101 } 102 103 func (m *Manager) getRepositoryBatch(ctx context.Context, repositoryID graveler.RepositoryID) (*graveler.RepositoryRecord, error) { 104 key := fmt.Sprintf("GetRepository:%s", repositoryID) 105 repository, err := m.batchExecutor.BatchFor(ctx, key, m.maxBatchDelay, batch.ExecuterFunc(func() (interface{}, error) { 106 return m.getRepository(context.Background(), repositoryID) 107 })) 108 if err != nil { 109 return nil, err 110 } 111 return repository.(*graveler.RepositoryRecord), nil 112 } 113 114 func (m *Manager) GetRepository(ctx context.Context, repositoryID graveler.RepositoryID) (*graveler.RepositoryRecord, error) { 115 rec, err := m.repoCache.GetOrSet(repositoryID, func() (interface{}, error) { 116 repo, err := m.getRepositoryBatch(ctx, repositoryID) 117 if err != nil { 118 return nil, err 119 } 120 121 switch repo.State { 122 case graveler.RepositoryState_ACTIVE: 123 return repo, nil 124 case graveler.RepositoryState_IN_DELETION: 125 return nil, graveler.ErrRepositoryInDeletion 126 default: 127 return nil, fmt.Errorf("invalid repository state (%d) rec: %w", repo.State, graveler.ErrInvalid) 128 } 129 }) 130 if err != nil { 131 return nil, err 132 } 133 return rec.(*graveler.RepositoryRecord), nil 134 } 135 136 func (m *Manager) createBareRepository(ctx context.Context, repositoryID graveler.RepositoryID, repository graveler.Repository) (*graveler.RepositoryRecord, error) { 137 repoRecord := &graveler.RepositoryRecord{ 138 RepositoryID: repositoryID, 139 Repository: &repository, 140 } 141 repo := graveler.ProtoFromRepo(repoRecord) 142 143 err := kv.SetMsgIf(ctx, m.kvStore, graveler.RepositoriesPartition(), []byte(graveler.RepoPath(repositoryID)), repo, nil) 144 if err != nil { 145 if errors.Is(err, kv.ErrPredicateFailed) { 146 err = graveler.ErrNotUnique 147 } 148 return nil, err 149 } 150 151 return repoRecord, nil 152 } 153 154 func (m *Manager) CreateRepository(ctx context.Context, repositoryID graveler.RepositoryID, repository graveler.Repository) (*graveler.RepositoryRecord, error) { 155 firstCommit := graveler.NewCommit() 156 firstCommit.Message = graveler.FirstCommitMsg 157 firstCommit.Generation = 1 158 159 repo := &graveler.RepositoryRecord{ 160 RepositoryID: repositoryID, 161 Repository: &repository, 162 } 163 // If branch creation fails - this commit will become dangling. This is a known issue that can be resolved via garbage collection 164 commitID, err := m.addCommit(ctx, graveler.RepoPartition(repo), firstCommit) 165 if err != nil { 166 return nil, err 167 } 168 169 branch := graveler.Branch{ 170 CommitID: commitID, 171 StagingToken: graveler.GenerateStagingToken(repositoryID, repository.DefaultBranchID), 172 SealedTokens: nil, 173 } 174 err = m.createBranch(ctx, graveler.RepoPartition(repo), repository.DefaultBranchID, branch) 175 if err != nil { 176 return nil, err 177 } 178 _, err = m.createBareRepository(ctx, repositoryID, repository) 179 if err != nil { 180 return nil, err 181 } 182 return repo, nil 183 } 184 185 func (m *Manager) CreateBareRepository(ctx context.Context, repositoryID graveler.RepositoryID, repository graveler.Repository) (*graveler.RepositoryRecord, error) { 186 return m.createBareRepository(ctx, repositoryID, repository) 187 } 188 189 func (m *Manager) ListRepositories(ctx context.Context) (graveler.RepositoryIterator, error) { 190 return NewRepositoryIterator(ctx, m.kvStore) 191 } 192 193 func (m *Manager) updateRepoState(ctx context.Context, repo *graveler.RepositoryRecord, state graveler.RepositoryState) error { 194 repo.State = state 195 return kv.SetMsg(ctx, m.kvStore, graveler.RepositoriesPartition(), []byte(graveler.RepoPath(repo.RepositoryID)), graveler.ProtoFromRepo(repo)) 196 } 197 198 func (m *Manager) deleteRepositoryBranches(ctx context.Context, repository *graveler.RepositoryRecord) error { 199 itr, err := m.ListBranches(ctx, repository) 200 if err != nil { 201 return err 202 } 203 defer itr.Close() 204 var wg multierror.Group 205 for itr.Next() { 206 b := itr.Value() 207 wg.Go(func() error { 208 return m.DeleteBranch(ctx, repository, b.BranchID) 209 }) 210 } 211 return wg.Wait().ErrorOrNil() 212 } 213 214 func (m *Manager) deleteRepositoryTags(ctx context.Context, repository *graveler.RepositoryRecord) error { 215 itr, err := m.ListTags(ctx, repository) 216 if err != nil { 217 return err 218 } 219 defer itr.Close() 220 var wg multierror.Group 221 for itr.Next() { 222 tag := itr.Value() 223 wg.Go(func() error { 224 return m.DeleteTag(ctx, repository, tag.TagID) 225 }) 226 } 227 return wg.Wait().ErrorOrNil() 228 } 229 230 func (m *Manager) deleteRepositoryCommits(ctx context.Context, repository *graveler.RepositoryRecord) error { 231 itr, err := m.ListCommits(ctx, repository) 232 if err != nil { 233 return err 234 } 235 defer itr.Close() 236 var wg multierror.Group 237 for itr.Next() { 238 commit := itr.Value() 239 wg.Go(func() error { 240 return m.RemoveCommit(ctx, repository, commit.CommitID) 241 }) 242 } 243 return wg.Wait().ErrorOrNil() 244 } 245 246 func (m *Manager) deleteRepositoryMetadata(ctx context.Context, repository *graveler.RepositoryRecord) error { 247 return m.kvStore.Delete(ctx, []byte(graveler.RepoPartition(repository)), []byte(graveler.RepoMetadataPath())) 248 } 249 250 func (m *Manager) deleteRepository(ctx context.Context, repo *graveler.RepositoryRecord) error { 251 // ctx := context.Background() TODO (niro): When running this async create a new context and remove ctx from signature 252 var wg multierror.Group 253 wg.Go(func() error { 254 return m.deleteRepositoryBranches(ctx, repo) 255 }) 256 wg.Go(func() error { 257 return m.deleteRepositoryTags(ctx, repo) 258 }) 259 wg.Go(func() error { 260 return m.deleteRepositoryCommits(ctx, repo) 261 }) 262 wg.Go(func() error { 263 return m.deleteRepositoryMetadata(ctx, repo) 264 }) 265 266 if err := wg.Wait().ErrorOrNil(); err != nil { 267 return err 268 } 269 270 // Finally delete the repository record itself 271 return m.kvStore.Delete(ctx, []byte(graveler.RepositoriesPartition()), []byte(graveler.RepoPath(repo.RepositoryID))) 272 } 273 274 func (m *Manager) DeleteRepository(ctx context.Context, repositoryID graveler.RepositoryID, opts ...graveler.SetOptionsFunc) error { 275 repo, err := m.getRepository(ctx, repositoryID) 276 if err != nil { 277 return err 278 } 279 280 options := &graveler.SetOptions{} 281 for _, opt := range opts { 282 opt(options) 283 } 284 if repo.ReadOnly && !options.Force { 285 return graveler.ErrReadOnlyRepository 286 } 287 288 // Set repository state to deleted and then perform background delete. 289 if repo.State != graveler.RepositoryState_IN_DELETION { 290 err = m.updateRepoState(ctx, repo, graveler.RepositoryState_IN_DELETION) 291 if err != nil { 292 return err 293 } 294 } 295 296 // TODO(niro): This should be a background delete process 297 return m.deleteRepository(ctx, repo) 298 } 299 300 func (m *Manager) getRepositoryMetadata(ctx context.Context, repo *graveler.RepositoryRecord) (graveler.RepositoryMetadata, kv.Predicate, error) { 301 data := graveler.RepoMetadata{} 302 pred, err := kv.GetMsg(ctx, m.kvStore, graveler.RepoPartition(repo), []byte(graveler.RepoMetadataPath()), &data) 303 if err != nil { 304 return nil, nil, err 305 } 306 return graveler.RepoMetadataFromProto(&data), pred, nil 307 } 308 309 func (m *Manager) GetRepositoryMetadata(ctx context.Context, repositoryID graveler.RepositoryID) (graveler.RepositoryMetadata, error) { 310 repo, err := m.getRepository(ctx, repositoryID) 311 if err != nil { 312 return nil, err 313 } 314 315 metadata, _, err := m.getRepositoryMetadata(ctx, repo) 316 if errors.Is(err, kv.ErrNotFound) { // Return nil map if not exists 317 return nil, nil 318 } 319 if err != nil { 320 return nil, err 321 } 322 323 return metadata, nil 324 } 325 326 func (m *Manager) SetRepositoryMetadata(ctx context.Context, repo *graveler.RepositoryRecord, updateFunc graveler.RepoMetadataUpdateFunc) error { 327 metadata, pred, err := m.getRepositoryMetadata(ctx, repo) 328 if errors.Is(err, kv.ErrNotFound) { // Create new metadata map and set predicate to nil for setIf not exists 329 metadata = graveler.RepositoryMetadata{} 330 pred = nil 331 } else if err != nil { 332 return err 333 } 334 335 newMetadata, err := updateFunc(metadata) 336 // return on error or nothing to update 337 if err != nil || newMetadata == nil { 338 return err 339 } 340 return kv.SetMsgIf(ctx, m.kvStore, graveler.RepoPartition(repo), []byte(graveler.RepoMetadataPath()), graveler.ProtoFromRepositoryMetadata(newMetadata), pred) 341 } 342 343 func (m *Manager) ParseRef(ref graveler.Ref) (graveler.RawRef, error) { 344 return ParseRef(ref) 345 } 346 347 func (m *Manager) ResolveRawRef(ctx context.Context, repository *graveler.RepositoryRecord, raw graveler.RawRef) (*graveler.ResolvedRef, error) { 348 return ResolveRawRef(ctx, m, m.addressProvider, repository, raw) 349 } 350 351 func (m *Manager) getBranchWithPredicate(ctx context.Context, repository *graveler.RepositoryRecord, branchID graveler.BranchID) (*graveler.Branch, kv.Predicate, error) { 352 key := fmt.Sprintf("GetBranch:%s:%s", repository.RepositoryID, branchID) 353 type branchPred struct { 354 *graveler.Branch 355 kv.Predicate 356 } 357 result, err := m.batchExecutor.BatchFor(ctx, key, m.maxBatchDelay, batch.ExecuterFunc(func() (interface{}, error) { 358 key := graveler.BranchPath(branchID) 359 data := graveler.BranchData{} 360 pred, err := kv.GetMsg(context.Background(), m.kvStore, graveler.RepoPartition(repository), []byte(key), &data) 361 if err != nil { 362 return nil, err 363 } 364 return &branchPred{Branch: branchFromProto(&data), Predicate: pred}, nil 365 })) 366 if errors.Is(err, kv.ErrNotFound) { 367 err = graveler.ErrBranchNotFound 368 } 369 if err != nil { 370 return nil, nil, err 371 } 372 branchWithPred := result.(*branchPred) 373 return branchWithPred.Branch, branchWithPred.Predicate, nil 374 } 375 376 func (m *Manager) GetBranch(ctx context.Context, repository *graveler.RepositoryRecord, branchID graveler.BranchID) (*graveler.Branch, error) { 377 branch, _, err := m.getBranchWithPredicate(ctx, repository, branchID) 378 return branch, err 379 } 380 381 func (m *Manager) createBranch(ctx context.Context, repositoryPartition string, branchID graveler.BranchID, branch graveler.Branch) error { 382 err := kv.SetMsgIf(ctx, m.kvStore, repositoryPartition, []byte(graveler.BranchPath(branchID)), protoFromBranch(branchID, &branch), nil) 383 if errors.Is(err, kv.ErrPredicateFailed) { 384 err = graveler.ErrBranchExists 385 } 386 return err 387 } 388 389 func (m *Manager) CreateBranch(ctx context.Context, repository *graveler.RepositoryRecord, branchID graveler.BranchID, branch graveler.Branch) error { 390 return m.createBranch(ctx, graveler.RepoPartition(repository), branchID, branch) 391 } 392 393 func (m *Manager) SetBranch(ctx context.Context, repository *graveler.RepositoryRecord, branchID graveler.BranchID, branch graveler.Branch) error { 394 return kv.SetMsg(ctx, m.kvStore, graveler.RepoPartition(repository), []byte(graveler.BranchPath(branchID)), protoFromBranch(branchID, &branch)) 395 } 396 397 func (m *Manager) BranchUpdate(ctx context.Context, repository *graveler.RepositoryRecord, branchID graveler.BranchID, f graveler.BranchUpdateFunc) error { 398 b, pred, err := m.getBranchWithPredicate(ctx, repository, branchID) 399 if err != nil { 400 return err 401 } 402 newBranch, err := f(b) 403 // return on error or nothing to update 404 if err != nil || newBranch == nil { 405 return err 406 } 407 return kv.SetMsgIf(ctx, m.kvStore, graveler.RepoPartition(repository), []byte(graveler.BranchPath(branchID)), protoFromBranch(branchID, newBranch), pred) 408 } 409 410 func (m *Manager) DeleteBranch(ctx context.Context, repository *graveler.RepositoryRecord, branchID graveler.BranchID) error { 411 _, err := m.GetBranch(ctx, repository, branchID) 412 if err != nil { 413 return err 414 } 415 return m.kvStore.Delete(ctx, []byte(graveler.RepoPartition(repository)), []byte(graveler.BranchPath(branchID))) 416 } 417 418 func (m *Manager) ListBranches(ctx context.Context, repository *graveler.RepositoryRecord) (graveler.BranchIterator, error) { 419 return NewBranchSimpleIterator(ctx, m.kvStore, repository) 420 } 421 422 func (m *Manager) GCBranchIterator(ctx context.Context, repository *graveler.RepositoryRecord) (graveler.BranchIterator, error) { 423 return NewBranchByCommitIterator(ctx, m.kvStore, repository) 424 } 425 426 func (m *Manager) GetTag(ctx context.Context, repository *graveler.RepositoryRecord, tagID graveler.TagID) (*graveler.CommitID, error) { 427 key := fmt.Sprintf("GetTag:%s:%s", repository.RepositoryID, tagID) 428 commitID, err := m.batchExecutor.BatchFor(ctx, key, m.maxBatchDelay, batch.ExecuterFunc(func() (interface{}, error) { 429 tagKey := graveler.TagPath(tagID) 430 t := graveler.TagData{} 431 _, err := kv.GetMsg(context.Background(), m.kvStore, graveler.RepoPartition(repository), []byte(tagKey), &t) 432 if err != nil { 433 return nil, err 434 } 435 commitID := graveler.CommitID(t.CommitId) 436 return &commitID, nil 437 })) 438 if errors.Is(err, kv.ErrNotFound) { 439 err = graveler.ErrTagNotFound 440 } 441 if err != nil { 442 return nil, err 443 } 444 return commitID.(*graveler.CommitID), nil 445 } 446 447 func (m *Manager) CreateTag(ctx context.Context, repository *graveler.RepositoryRecord, tagID graveler.TagID, commitID graveler.CommitID) error { 448 t := &graveler.TagData{ 449 Id: tagID.String(), 450 CommitId: commitID.String(), 451 } 452 tagKey := graveler.TagPath(tagID) 453 err := kv.SetMsgIf(ctx, m.kvStore, graveler.RepoPartition(repository), []byte(tagKey), t, nil) 454 if err != nil { 455 if errors.Is(err, kv.ErrPredicateFailed) { 456 err = graveler.ErrTagAlreadyExists 457 } 458 return err 459 } 460 return nil 461 } 462 463 func (m *Manager) DeleteTag(ctx context.Context, repository *graveler.RepositoryRecord, tagID graveler.TagID) error { 464 tagKey := graveler.TagPath(tagID) 465 // TODO (issue 3640) align with delete tag DB - return ErrNotFound when tag does not exist 466 return m.kvStore.Delete(ctx, []byte(graveler.RepoPartition(repository)), []byte(tagKey)) 467 } 468 469 func (m *Manager) ListTags(ctx context.Context, repository *graveler.RepositoryRecord) (graveler.TagIterator, error) { 470 return NewTagIterator(ctx, m.kvStore, repository) 471 } 472 473 func (m *Manager) GetCommitByPrefix(ctx context.Context, repository *graveler.RepositoryRecord, prefix graveler.CommitID) (*graveler.Commit, error) { 474 // optimize by get if prefix is not a prefix, but a full length commit id 475 if len(prefix) == commitIDStringLength { 476 return m.GetCommit(ctx, repository, prefix) 477 } 478 key := fmt.Sprintf("GetCommitByPrefix:%s:%s", repository.RepositoryID, prefix) 479 commit, err := m.batchExecutor.BatchFor(ctx, key, m.maxBatchDelay, batch.ExecuterFunc(func() (interface{}, error) { 480 it, err := NewOrderedCommitIterator(context.Background(), m.kvStore, repository, false) 481 if err != nil { 482 return nil, err 483 } 484 defer it.Close() 485 it.SeekGE(prefix) 486 var commit *graveler.Commit 487 for it.Next() { 488 c := it.Value() 489 if strings.HasPrefix(string(c.CommitID), string(prefix)) { 490 if commit != nil { 491 return nil, graveler.ErrCommitNotFound // more than 1 commit starts with the ID prefix 492 } 493 commit = c.Commit 494 } else { 495 break 496 } 497 } 498 if err := it.Err(); err != nil { 499 return nil, err 500 } 501 if commit == nil { 502 return nil, graveler.ErrCommitNotFound 503 } 504 return commit, nil 505 })) 506 if err != nil { 507 return nil, err 508 } 509 return commit.(*graveler.Commit), nil 510 } 511 512 func (m *Manager) GetCommit(ctx context.Context, repository *graveler.RepositoryRecord, commitID graveler.CommitID) (*graveler.Commit, error) { 513 key := fmt.Sprintf("%s:%s", repository.RepositoryID, commitID) 514 v, err := m.commitCache.GetOrSet(key, func() (v interface{}, err error) { 515 return m.getCommitBatch(ctx, repository, commitID) 516 }) 517 if err != nil { 518 return nil, err 519 } 520 return v.(*graveler.Commit), nil 521 } 522 523 func (m *Manager) getCommitBatch(ctx context.Context, repository *graveler.RepositoryRecord, commitID graveler.CommitID) (*graveler.Commit, error) { 524 key := fmt.Sprintf("GetCommit:%s:%s", repository.RepositoryID, commitID) 525 commit, err := m.batchExecutor.BatchFor(ctx, key, m.maxBatchDelay, batch.ExecuterFunc(func() (interface{}, error) { 526 return m.getCommit(context.Background(), commitID, repository) 527 })) 528 if err != nil { 529 return nil, err 530 } 531 return commit.(*graveler.Commit), nil 532 } 533 534 func (m *Manager) getCommit(ctx context.Context, commitID graveler.CommitID, repository *graveler.RepositoryRecord) (interface{}, error) { 535 commitKey := graveler.CommitPath(commitID) 536 c := graveler.CommitData{} 537 _, err := kv.GetMsg(ctx, m.kvStore, graveler.RepoPartition(repository), []byte(commitKey), &c) 538 if errors.Is(err, kv.ErrNotFound) { 539 err = graveler.ErrCommitNotFound 540 } 541 if err != nil { 542 return nil, err 543 } 544 return graveler.CommitFromProto(&c), nil 545 } 546 547 func (m *Manager) addCommit(ctx context.Context, repoPartition string, commit graveler.Commit) (graveler.CommitID, error) { 548 commitID := m.addressProvider.ContentAddress(commit) 549 c := graveler.ProtoFromCommit(graveler.CommitID(commitID), &commit) 550 commitKey := graveler.CommitPath(graveler.CommitID(commitID)) 551 err := kv.SetMsgIf(ctx, m.kvStore, repoPartition, []byte(commitKey), c, nil) 552 // commits are written based on their content hash, if we insert the same ID again, 553 // it will necessarily have the same attributes as the existing one, so if a commit already exists doesn't return an error 554 if err != nil && !errors.Is(err, kv.ErrPredicateFailed) { 555 return "", err 556 } 557 return graveler.CommitID(commitID), nil 558 } 559 560 func (m *Manager) AddCommit(ctx context.Context, repository *graveler.RepositoryRecord, commit graveler.Commit) (graveler.CommitID, error) { 561 return m.addCommit(ctx, graveler.RepoPartition(repository), commit) 562 } 563 564 func (m *Manager) CreateCommitRecord(ctx context.Context, repository *graveler.RepositoryRecord, commitID graveler.CommitID, commit graveler.Commit) error { 565 if m.addressProvider.ContentAddress(commit) != commitID.String() { 566 return graveler.ErrInvalidCommitID 567 } 568 c := graveler.ProtoFromCommit(commitID, &commit) 569 commitKey := graveler.CommitPath(commitID) 570 err := kv.SetMsgIf(ctx, m.kvStore, graveler.RepoPartition(repository), []byte(commitKey), c, nil) 571 if errors.Is(err, kv.ErrPredicateFailed) { 572 return graveler.ErrCommitAlreadyExists 573 } else if err != nil { 574 return err 575 } 576 return nil 577 } 578 579 func (m *Manager) RemoveCommit(ctx context.Context, repository *graveler.RepositoryRecord, commitID graveler.CommitID) error { 580 commitKey := graveler.CommitPath(commitID) 581 return m.kvStore.Delete(ctx, []byte(graveler.RepoPartition(repository)), []byte(commitKey)) 582 } 583 584 func (m *Manager) FindMergeBase(ctx context.Context, repository *graveler.RepositoryRecord, commitIDs ...graveler.CommitID) (*graveler.Commit, error) { 585 const allowedCommitsToCompare = 2 586 if len(commitIDs) != allowedCommitsToCompare { 587 return nil, graveler.ErrInvalidMergeBase 588 } 589 return FindMergeBase(ctx, m, repository, commitIDs[0], commitIDs[1]) 590 } 591 592 func (m *Manager) Log(ctx context.Context, repository *graveler.RepositoryRecord, from graveler.CommitID, firstParent bool, since *time.Time) (graveler.CommitIterator, error) { 593 return NewCommitIterator(ctx, &CommitIteratorConfig{ 594 repository: repository, 595 start: from, 596 firstParent: firstParent, 597 since: since, 598 manager: m, 599 }), nil 600 } 601 602 func (m *Manager) ListCommits(ctx context.Context, repository *graveler.RepositoryRecord) (graveler.CommitIterator, error) { 603 return NewOrderedCommitIterator(ctx, m.kvStore, repository, false) 604 } 605 606 func (m *Manager) GCCommitIterator(ctx context.Context, repository *graveler.RepositoryRecord) (graveler.CommitIterator, error) { 607 return NewOrderedCommitIterator(ctx, m.kvStore, repository, true) 608 } 609 610 func newCache(cfg CacheConfig) cache.Cache { 611 if cfg.Size == 0 { 612 return cache.NoCache 613 } 614 return cache.NewCache(cfg.Size, cfg.Expiry, cache.NewJitterFn(cfg.Jitter)) 615 } 616 617 func (m *Manager) DeleteExpiredImports(ctx context.Context, repository *graveler.RepositoryRecord) error { 618 expiry := time.Now().Add(-ImportExpiryTime) 619 repoPartition := graveler.RepoPartition(repository) 620 key := []byte(graveler.ImportsPath("")) 621 options := kv.IteratorOptionsFrom([]byte("")) 622 itr, err := kv.NewPrimaryIterator(ctx, m.kvStoreLimited, (&graveler.ImportStatusData{}).ProtoReflect().Type(), repoPartition, key, options) 623 if err != nil { 624 return fmt.Errorf("failed to get imports iterator from store: %w", err) 625 } 626 defer itr.Close() 627 628 var errs multierror.Error 629 for itr.Next() { 630 entry := itr.Entry() 631 status, ok := entry.Value.(*graveler.ImportStatusData) 632 if !ok { 633 return fmt.Errorf("invalid protobuf type %s: %w", entry.Value.ProtoReflect().Type().Descriptor().FullName(), graveler.ErrReadingFromStore) 634 } 635 if status.UpdatedAt.AsTime().Before(expiry) { 636 if !status.Completed && status.Error == "" { 637 logging.FromContext(ctx).WithFields(logging.Fields{"import_id": status.Id}).Warning("removing stale import") 638 } 639 err = m.kvStoreLimited.Delete(ctx, []byte(repoPartition), entry.Key) 640 if err != nil { 641 errs.Errors = append(errs.Errors, fmt.Errorf("delete failed for import ID %s: %w", status.Id, err)) 642 } 643 } 644 } 645 return errs.ErrorOrNil() 646 }