add bulk users
This commit is contained in:
@@ -2,9 +2,12 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
"eslogad-be/internal/entities"
|
||||
"eslogad-be/internal/manager"
|
||||
"eslogad-be/internal/transformer"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -41,18 +44,16 @@ func (s *UserServiceImpl) BulkCreateUsers(ctx context.Context, req *contract.Bul
|
||||
},
|
||||
}
|
||||
|
||||
// Process in batches to avoid memory and database issues
|
||||
batchSize := 50
|
||||
for i := 0; i < len(req.Users); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(req.Users) {
|
||||
end = len(req.Users)
|
||||
}
|
||||
|
||||
|
||||
batch := req.Users[i:end]
|
||||
batchResults, err := s.processBulkUserBatch(ctx, batch)
|
||||
if err != nil {
|
||||
// Log batch error but continue with other batches
|
||||
for _, userReq := range batch {
|
||||
response.Failed = append(response.Failed, contract.BulkUserErrorResult{
|
||||
User: userReq,
|
||||
@@ -62,7 +63,7 @@ func (s *UserServiceImpl) BulkCreateUsers(ctx context.Context, req *contract.Bul
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
response.Created = append(response.Created, batchResults.Created...)
|
||||
response.Failed = append(response.Failed, batchResults.Failed...)
|
||||
response.Summary.Succeeded += batchResults.Summary.Succeeded
|
||||
@@ -72,6 +73,121 @@ func (s *UserServiceImpl) BulkCreateUsers(ctx context.Context, req *contract.Bul
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceImpl) BulkCreateUsersAsync(ctx context.Context, req *contract.BulkCreateUsersRequest) (*contract.BulkCreateAsyncResponse, error) {
|
||||
jobManager := manager.GetJobManager()
|
||||
jobID := jobManager.CreateJob()
|
||||
|
||||
// Start async processing
|
||||
go s.processBulkUsersAsync(context.Background(), jobID, req)
|
||||
|
||||
return &contract.BulkCreateAsyncResponse{
|
||||
JobID: jobID,
|
||||
Message: fmt.Sprintf("Job started for %d users", len(req.Users)),
|
||||
Status: "processing",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceImpl) processBulkUsersAsync(ctx context.Context, jobID uuid.UUID, req *contract.BulkCreateUsersRequest) {
|
||||
jobManager := manager.GetJobManager()
|
||||
jobManager.UpdateJob(jobID, manager.JobStatusProcessing, fmt.Sprintf("Processing %d users", len(req.Users)))
|
||||
|
||||
batchSize := 50
|
||||
var wg sync.WaitGroup
|
||||
resultChan := make(chan *contract.BulkCreateUsersResponse, (len(req.Users)/batchSize)+1)
|
||||
|
||||
// Process each batch independently in its own goroutine
|
||||
for i := 0; i < len(req.Users); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(req.Users) {
|
||||
end = len(req.Users)
|
||||
}
|
||||
|
||||
batch := req.Users[i:end]
|
||||
wg.Add(1)
|
||||
|
||||
// Launch goroutine for each batch
|
||||
go func(batchNum int, users []contract.BulkUserRequest) {
|
||||
defer wg.Done()
|
||||
|
||||
batchResult := &contract.BulkCreateUsersResponse{
|
||||
Created: []contract.UserResponse{},
|
||||
Failed: []contract.BulkUserErrorResult{},
|
||||
Summary: contract.BulkCreationSummary{
|
||||
Total: len(users),
|
||||
Succeeded: 0,
|
||||
Failed: 0,
|
||||
},
|
||||
}
|
||||
|
||||
// Process batch
|
||||
created, failed, err := s.userProcessor.BulkCreateUsersWithTransaction(ctx, users)
|
||||
if err != nil {
|
||||
// If entire batch fails, mark all users as failed
|
||||
for _, userReq := range users {
|
||||
batchResult.Failed = append(batchResult.Failed, contract.BulkUserErrorResult{
|
||||
User: userReq,
|
||||
Error: fmt.Sprintf("Batch %d error: %v", batchNum, err),
|
||||
})
|
||||
batchResult.Summary.Failed++
|
||||
}
|
||||
} else {
|
||||
batchResult.Created = created
|
||||
batchResult.Failed = failed
|
||||
batchResult.Summary.Succeeded = len(created)
|
||||
batchResult.Summary.Failed = len(failed)
|
||||
}
|
||||
|
||||
resultChan <- batchResult
|
||||
}(i/batchSize, batch)
|
||||
}
|
||||
|
||||
// Wait for all batches to complete
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(resultChan)
|
||||
}()
|
||||
|
||||
// Aggregate results
|
||||
totalSummary := contract.BulkCreationSummary{
|
||||
Total: len(req.Users),
|
||||
Succeeded: 0,
|
||||
Failed: 0,
|
||||
}
|
||||
allCreated := []contract.UserResponse{}
|
||||
allFailed := []contract.BulkUserErrorResult{}
|
||||
|
||||
for result := range resultChan {
|
||||
allCreated = append(allCreated, result.Created...)
|
||||
allFailed = append(allFailed, result.Failed...)
|
||||
totalSummary.Succeeded += result.Summary.Succeeded
|
||||
totalSummary.Failed += result.Summary.Failed
|
||||
|
||||
// Update job progress
|
||||
jobManager.UpdateJobResults(jobID, result.Created, result.Failed, result.Summary)
|
||||
}
|
||||
|
||||
// Mark job as completed
|
||||
status := manager.JobStatusCompleted
|
||||
message := fmt.Sprintf("Completed: %d succeeded, %d failed out of %d total",
|
||||
totalSummary.Succeeded, totalSummary.Failed, totalSummary.Total)
|
||||
|
||||
if totalSummary.Failed == totalSummary.Total {
|
||||
status = manager.JobStatusFailed
|
||||
message = "All user creations failed"
|
||||
}
|
||||
|
||||
jobManager.UpdateJob(jobID, status, message)
|
||||
}
|
||||
|
||||
func (s *UserServiceImpl) GetBulkJobStatus(ctx context.Context, jobID uuid.UUID) (*manager.BulkJobResult, error) {
|
||||
jobManager := manager.GetJobManager()
|
||||
job, exists := jobManager.GetJob(jobID)
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("job not found: %s", jobID)
|
||||
}
|
||||
return job, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceImpl) processBulkUserBatch(ctx context.Context, batch []contract.BulkUserRequest) (*contract.BulkCreateUsersResponse, error) {
|
||||
response := &contract.BulkCreateUsersResponse{
|
||||
Created: []contract.UserResponse{},
|
||||
|
||||
Reference in New Issue
Block a user