Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
81ae4251
Commit
81ae4251
authored
May 27, 2025
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: fix linter
parent
eca91d5c
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
58 additions
and
59 deletions
+58
-59
profiler.go
server/profiler/profiler.go
+10
-10
cache.go
store/cache/cache.go
+48
-49
No files found.
server/profiler/profiler.go
View file @
81ae4251
...
...
@@ -12,19 +12,19 @@ import (
"github.com/labstack/echo/v4"
)
// Profiler provides HTTP endpoints for memory profiling
// Profiler provides HTTP endpoints for memory profiling
.
type
Profiler
struct
{
memStatsLogInterval
time
.
Duration
}
// NewProfiler creates a new profiler
// NewProfiler creates a new profiler
.
func
NewProfiler
()
*
Profiler
{
return
&
Profiler
{
memStatsLogInterval
:
1
*
time
.
Minute
,
}
}
// RegisterRoutes adds profiling endpoints to the Echo server
// RegisterRoutes adds profiling endpoints to the Echo server
.
func
(
p
*
Profiler
)
RegisterRoutes
(
e
*
echo
.
Echo
)
{
// Register pprof handlers
g
:=
e
.
Group
(
"/debug/pprof"
)
...
...
@@ -41,7 +41,7 @@ func (p *Profiler) RegisterRoutes(e *echo.Echo) {
g
.
GET
(
"/mutex"
,
echo
.
WrapHandler
(
http
.
HandlerFunc
(
pprof
.
Handler
(
"mutex"
)
.
ServeHTTP
)))
g
.
GET
(
"/threadcreate"
,
echo
.
WrapHandler
(
http
.
HandlerFunc
(
pprof
.
Handler
(
"threadcreate"
)
.
ServeHTTP
)))
// Add a custom memory stats endpoint
// Add a custom memory stats endpoint
.
g
.
GET
(
"/memstats"
,
func
(
c
echo
.
Context
)
error
{
var
m
runtime
.
MemStats
runtime
.
ReadMemStats
(
&
m
)
...
...
@@ -58,13 +58,13 @@ func (p *Profiler) RegisterRoutes(e *echo.Echo) {
})
}
// StartMemoryMonitor starts a goroutine that periodically logs memory stats
// StartMemoryMonitor starts a goroutine that periodically logs memory stats
.
func
(
p
*
Profiler
)
StartMemoryMonitor
(
ctx
context
.
Context
)
{
go
func
()
{
ticker
:=
time
.
NewTicker
(
p
.
memStatsLogInterval
)
defer
ticker
.
Stop
()
// Store previous heap allocation to track growth
// Store previous heap allocation to track growth
.
var
lastHeapAlloc
uint64
var
lastNumGC
uint32
...
...
@@ -74,7 +74,7 @@ func (p *Profiler) StartMemoryMonitor(ctx context.Context) {
var
m
runtime
.
MemStats
runtime
.
ReadMemStats
(
&
m
)
// Calculate heap growth since last check
// Calculate heap growth since last check
.
heapGrowth
:=
int64
(
m
.
HeapAlloc
)
-
int64
(
lastHeapAlloc
)
gcCount
:=
m
.
NumGC
-
lastNumGC
...
...
@@ -90,11 +90,11 @@ func (p *Profiler) StartMemoryMonitor(ctx context.Context) {
"gcPause"
,
time
.
Duration
(
m
.
PauseNs
[(
m
.
NumGC
+
255
)
%
256
])
.
String
(),
)
// Track values for next iteration
// Track values for next iteration
.
lastHeapAlloc
=
m
.
HeapAlloc
lastNumGC
=
m
.
NumGC
// Force GC if memory usage is high to see if objects can be reclaimed
// Force GC if memory usage is high to see if objects can be reclaimed
.
if
m
.
HeapAlloc
>
500
*
1024
*
1024
{
// 500 MB threshold
slog
.
Info
(
"forcing garbage collection due to high memory usage"
)
runtime
.
GC
()
...
...
@@ -106,7 +106,7 @@ func (p *Profiler) StartMemoryMonitor(ctx context.Context) {
}()
}
// byteCountIEC converts bytes to a human-readable string (MiB, GiB)
// byteCountIEC converts bytes to a human-readable string (MiB, GiB)
.
func
byteCountIEC
(
b
uint64
)
string
{
const
unit
=
1024
if
b
<
unit
{
...
...
store/cache/cache.go
View file @
81ae4251
...
...
@@ -7,53 +7,53 @@ import (
"time"
)
// Interface defines the operations a cache must support
// Interface defines the operations a cache must support
.
type
Interface
interface
{
// Set adds a value to the cache with the default TTL
Set
(
ctx
context
.
Context
,
key
string
,
value
interface
{}
)
// Set adds a value to the cache with the default TTL
.
Set
(
ctx
context
.
Context
,
key
string
,
value
any
)
// SetWithTTL adds a value to the cache with a custom TTL
SetWithTTL
(
ctx
context
.
Context
,
key
string
,
value
interface
{}
,
ttl
time
.
Duration
)
// SetWithTTL adds a value to the cache with a custom TTL
.
SetWithTTL
(
ctx
context
.
Context
,
key
string
,
value
any
,
ttl
time
.
Duration
)
// Get retrieves a value from the cache
Get
(
ctx
context
.
Context
,
key
string
)
(
interface
{}
,
bool
)
// Get retrieves a value from the cache
.
Get
(
ctx
context
.
Context
,
key
string
)
(
any
,
bool
)
// Delete removes a value from the cache
// Delete removes a value from the cache
.
Delete
(
ctx
context
.
Context
,
key
string
)
// Clear removes all values from the cache
// Clear removes all values from the cache
.
Clear
(
ctx
context
.
Context
)
// Size returns the number of items in the cache
// Size returns the number of items in the cache
.
Size
()
int64
// Close stops all background tasks and releases resources
// Close stops all background tasks and releases resources
.
Close
()
error
}
// item represents a cached value with metadata
// item represents a cached value with metadata
.
type
item
struct
{
value
interface
{}
value
any
expiration
time
.
Time
size
int
// Approximate size in bytes
}
// Config contains options for configuring a cache
// Config contains options for configuring a cache
.
type
Config
struct
{
// DefaultTTL is the default time-to-live for cache entries
// DefaultTTL is the default time-to-live for cache entries
.
DefaultTTL
time
.
Duration
// CleanupInterval is how often the cache runs cleanup
// CleanupInterval is how often the cache runs cleanup
.
CleanupInterval
time
.
Duration
// MaxItems is the maximum number of items allowed in the cache
// MaxItems is the maximum number of items allowed in the cache
.
MaxItems
int
// OnEviction is called when an item is evicted from the cache
OnEviction
func
(
key
string
,
value
interface
{}
)
// OnEviction is called when an item is evicted from the cache
.
OnEviction
func
(
key
string
,
value
any
)
}
// DefaultConfig returns a default configuration for the cache
// DefaultConfig returns a default configuration for the cache
.
func
DefaultConfig
()
Config
{
return
Config
{
DefaultTTL
:
10
*
time
.
Minute
,
...
...
@@ -63,7 +63,7 @@ func DefaultConfig() Config {
}
}
// Cache is a thread-safe in-memory cache with TTL and memory management
// Cache is a thread-safe in-memory cache with TTL and memory management
.
type
Cache
struct
{
data
sync
.
Map
config
Config
...
...
@@ -72,7 +72,7 @@ type Cache struct {
closedChan
chan
struct
{}
}
// New creates a new memory cache with the given configuration
// New creates a new memory cache with the given configuration
.
func
New
(
config
Config
)
*
Cache
{
c
:=
&
Cache
{
config
:
config
,
...
...
@@ -84,27 +84,26 @@ func New(config Config) *Cache {
return
c
}
// NewDefault creates a new memory cache with default configuration
// NewDefault creates a new memory cache with default configuration
.
func
NewDefault
()
*
Cache
{
return
New
(
DefaultConfig
())
}
// Set adds a value to the cache with the default TTL
func
(
c
*
Cache
)
Set
(
ctx
context
.
Context
,
key
string
,
value
interface
{}
)
{
// Set adds a value to the cache with the default TTL
.
func
(
c
*
Cache
)
Set
(
ctx
context
.
Context
,
key
string
,
value
any
)
{
c
.
SetWithTTL
(
ctx
,
key
,
value
,
c
.
config
.
DefaultTTL
)
}
// SetWithTTL adds a value to the cache with a custom TTL
func
(
c
*
Cache
)
SetWithTTL
(
ctx
context
.
Context
,
key
string
,
value
interface
{}
,
ttl
time
.
Duration
)
{
// Estimate size of the item (very rough approximation)
// SetWithTTL adds a value to the cache with a custom TTL
.
func
(
c
*
Cache
)
SetWithTTL
(
ctx
context
.
Context
,
key
string
,
value
any
,
ttl
time
.
Duration
)
{
// Estimate size of the item (very rough approximation)
.
size
:=
estimateSize
(
value
)
// Check if item already exists to avoid double counting
// Check if item already exists to avoid double counting
.
if
_
,
exists
:=
c
.
data
.
Load
(
key
);
exists
{
c
.
data
.
Delete
(
key
)
// Don't decrement count - we'll replace it
}
else
{
// Only increment if this is a new key
// Only increment if this is a new key
.
atomic
.
AddInt64
(
&
c
.
itemCount
,
1
)
}
...
...
@@ -114,14 +113,14 @@ func (c *Cache) SetWithTTL(ctx context.Context, key string, value interface{}, t
size
:
size
,
})
// If we're over the max items, clean up old items
// If we're over the max items, clean up old items
.
if
c
.
config
.
MaxItems
>
0
&&
atomic
.
LoadInt64
(
&
c
.
itemCount
)
>
int64
(
c
.
config
.
MaxItems
)
{
c
.
cleanupOldest
()
}
}
// Get retrieves a value from the cache
func
(
c
*
Cache
)
Get
(
ctx
context
.
Context
,
key
string
)
(
interface
{}
,
bool
)
{
// Get retrieves a value from the cache
.
func
(
c
*
Cache
)
Get
(
ctx
context
.
Context
,
key
string
)
(
any
,
bool
)
{
value
,
ok
:=
c
.
data
.
Load
(
key
)
if
!
ok
{
return
nil
,
false
...
...
@@ -142,7 +141,7 @@ func (c *Cache) Get(ctx context.Context, key string) (interface{}, bool) {
return
itm
.
value
,
true
}
// Delete removes a value from the cache
// Delete removes a value from the cache
.
func
(
c
*
Cache
)
Delete
(
ctx
context
.
Context
,
key
string
)
{
if
value
,
loaded
:=
c
.
data
.
LoadAndDelete
(
key
);
loaded
{
atomic
.
AddInt64
(
&
c
.
itemCount
,
-
1
)
...
...
@@ -154,10 +153,10 @@ func (c *Cache) Delete(ctx context.Context, key string) {
}
}
// Clear removes all values from the cache
// Clear removes all values from the cache
.
func
(
c
*
Cache
)
Clear
(
ctx
context
.
Context
)
{
if
c
.
config
.
OnEviction
!=
nil
{
c
.
data
.
Range
(
func
(
key
,
value
interface
{}
)
bool
{
c
.
data
.
Range
(
func
(
key
,
value
any
)
bool
{
itm
:=
value
.
(
item
)
c
.
config
.
OnEviction
(
key
.
(
string
),
itm
.
value
)
return
true
...
...
@@ -168,12 +167,12 @@ func (c *Cache) Clear(ctx context.Context) {
atomic
.
StoreInt64
(
&
c
.
itemCount
,
0
)
}
// Size returns the number of items in the cache
// Size returns the number of items in the cache
.
func
(
c
*
Cache
)
Size
()
int64
{
return
atomic
.
LoadInt64
(
&
c
.
itemCount
)
}
// Close stops the cache cleanup goroutine
// Close stops the cache cleanup goroutine
.
func
(
c
*
Cache
)
Close
()
error
{
select
{
case
<-
c
.
stopChan
:
...
...
@@ -186,7 +185,7 @@ func (c *Cache) Close() error {
}
}
// cleanupLoop periodically cleans up expired items
// cleanupLoop periodically cleans up expired items
.
func
(
c
*
Cache
)
cleanupLoop
()
{
ticker
:=
time
.
NewTicker
(
c
.
config
.
CleanupInterval
)
defer
func
()
{
...
...
@@ -204,12 +203,12 @@ func (c *Cache) cleanupLoop() {
}
}
// cleanup removes expired items
// cleanup removes expired items
.
func
(
c
*
Cache
)
cleanup
()
{
evicted
:=
make
(
map
[
string
]
interface
{}
)
evicted
:=
make
(
map
[
string
]
any
)
count
:=
0
c
.
data
.
Range
(
func
(
key
,
value
interface
{}
)
bool
{
c
.
data
.
Range
(
func
(
key
,
value
any
)
bool
{
itm
:=
value
.
(
item
)
if
time
.
Now
()
.
After
(
itm
.
expiration
)
{
c
.
data
.
Delete
(
key
)
...
...
@@ -234,7 +233,7 @@ func (c *Cache) cleanup() {
}
}
// cleanupOldest removes the oldest items if we're over the max items
// cleanupOldest removes the oldest items if we're over the max items
.
func
(
c
*
Cache
)
cleanupOldest
()
{
threshold
:=
c
.
config
.
MaxItems
/
5
// Remove 20% of max items at once
if
threshold
<
1
{
...
...
@@ -251,12 +250,12 @@ func (c *Cache) cleanupOldest() {
// Find the oldest items
type
keyExpPair
struct
{
key
string
value
interface
{}
value
any
expiration
time
.
Time
}
candidates
:=
make
([]
keyExpPair
,
0
,
threshold
)
c
.
data
.
Range
(
func
(
key
,
value
interface
{}
)
bool
{
c
.
data
.
Range
(
func
(
key
,
value
any
)
bool
{
itm
:=
value
.
(
item
)
if
len
(
candidates
)
<
threshold
{
candidates
=
append
(
candidates
,
keyExpPair
{
key
.
(
string
),
itm
.
value
,
itm
.
expiration
})
...
...
@@ -296,14 +295,14 @@ func (c *Cache) cleanupOldest() {
}
}
// estimateSize attempts to estimate the memory footprint of a value
func
estimateSize
(
value
interface
{}
)
int
{
// estimateSize attempts to estimate the memory footprint of a value
.
func
estimateSize
(
value
any
)
int
{
switch
v
:=
value
.
(
type
)
{
case
string
:
return
len
(
v
)
+
24
// base size + string overhead
case
[]
byte
:
return
len
(
v
)
+
24
// base size + slice overhead
case
map
[
string
]
interface
{}
:
case
map
[
string
]
any
:
return
len
(
v
)
*
64
// rough estimate
default
:
return
64
// default conservative estimate
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment