-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
349 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package queue | ||
|
||
import ( | ||
"encoding/gob" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path" | ||
"time" | ||
|
||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
func (q *PersistentGroupedQueue) compact() { | ||
q.mutex.Lock() | ||
defer q.mutex.Unlock() | ||
|
||
// Create a new segment for compacted data | ||
newSegmentName := fmt.Sprintf("segment_%d", time.Now().UnixNano()) | ||
newSegmentPath := path.Join(q.segmentDir, newSegmentName) | ||
newSegment, err := os.Create(newSegmentPath) | ||
if err != nil { | ||
q.logError("Failed to create new segment for compaction", err) | ||
return | ||
} | ||
defer newSegment.Close() | ||
|
||
newEncoder := gob.NewEncoder(newSegment) | ||
newHostIndex := make(map[string][]uint64) | ||
|
||
// Iterate through all active segments | ||
for _, segmentPath := range q.activeSegments { | ||
file, err := os.Open(segmentPath) | ||
if err != nil { | ||
q.logError("Failed to open segment for compaction", err) | ||
continue | ||
} | ||
defer file.Close() | ||
|
||
decoder := gob.NewDecoder(file) | ||
|
||
for { | ||
var item Item | ||
err := decoder.Decode(&item) | ||
if err == io.EOF { | ||
break | ||
} | ||
if err != nil { | ||
q.logError("Failed to decode item during compaction", err) | ||
continue | ||
} | ||
|
||
// Check if this item is still in the queue | ||
positions, exists := q.hostIndex[item.Host] | ||
if !exists { | ||
continue | ||
} | ||
|
||
itemPosition, err := file.Seek(0, io.SeekCurrent) | ||
if err != nil { | ||
q.logError("Failed to get current position in segment", err) | ||
continue | ||
} | ||
|
||
if !containsUint64(positions, uint64(itemPosition)) { | ||
continue | ||
} | ||
|
||
// Item is still in queue, write it to new segment | ||
newPosition, err := newSegment.Seek(0, io.SeekCurrent) | ||
if err != nil { | ||
q.logError("Failed to get position in new segment", err) | ||
continue | ||
} | ||
|
||
err = newEncoder.Encode(&item) | ||
if err != nil { | ||
q.logError("Failed to encode item to new segment", err) | ||
continue | ||
} | ||
|
||
newHostIndex[item.Host] = append(newHostIndex[item.Host], uint64(newPosition)) | ||
} | ||
} | ||
|
||
// Replace old segments with new one | ||
for _, segmentPath := range q.activeSegments { | ||
err := os.Remove(segmentPath) | ||
if err != nil { | ||
q.logError("Failed to remove old segment", err) | ||
} | ||
} | ||
|
||
// Update queue state | ||
q.activeSegments = []string{newSegmentPath} | ||
q.currentSegment.Close() | ||
q.currentSegment, err = os.OpenFile(newSegmentPath, os.O_RDWR, 0644) | ||
if err != nil { | ||
q.logError("Failed to open new segment after compaction", err) | ||
return | ||
} | ||
q.queueEncoder = gob.NewEncoder(q.currentSegment) | ||
q.queueDecoder = gob.NewDecoder(q.currentSegment) | ||
q.hostIndex = newHostIndex | ||
|
||
// Save updated metadata | ||
err = q.saveMetadata() | ||
if err != nil { | ||
q.logError("Failed to save metadata after compaction", err) | ||
} | ||
|
||
q.logInfo("Compaction completed successfully") | ||
} | ||
|
||
// Helper function to check if a uint64 slice contains a value | ||
func containsUint64(slice []uint64, value uint64) bool { | ||
for _, v := range slice { | ||
if v == value { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
// Helper function for logging errors | ||
func (q *PersistentGroupedQueue) logError(message string, err error) { | ||
if q.LoggingChan != nil { | ||
q.LoggingChan <- &LogMessage{ | ||
Level: logrus.ErrorLevel, | ||
Message: fmt.Sprintf("%s: %v", message, err), | ||
} | ||
} | ||
} | ||
|
||
// Helper function for logging info | ||
func (q *PersistentGroupedQueue) logInfo(message string) { | ||
if q.LoggingChan != nil { | ||
q.LoggingChan <- &LogMessage{ | ||
Level: logrus.InfoLevel, | ||
Message: message, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.