-
-
Notifications
You must be signed in to change notification settings - Fork 251
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: simplify exponential backoff and refactor env (#1185)
Co-authored-by: Kévin Dunglas <[email protected]>
- Loading branch information
1 parent
449a0e7
commit 1e279bc
Showing
5 changed files
with
178 additions
and
130 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,51 @@ | ||
package frankenphp | ||
|
||
import ( | ||
"sync" | ||
"time" | ||
) | ||
|
||
type exponentialBackoff struct { | ||
backoff time.Duration | ||
failureCount int | ||
mu sync.RWMutex | ||
maxBackoff time.Duration | ||
minBackoff time.Duration | ||
maxConsecutiveFailures int | ||
} | ||
|
||
// recordSuccess resets the backoff and failureCount | ||
func (e *exponentialBackoff) recordSuccess() { | ||
e.mu.Lock() | ||
e.failureCount = 0 | ||
e.backoff = e.minBackoff | ||
e.mu.Unlock() | ||
} | ||
|
||
// recordFailure increments the failure count and increases the backoff, it returns true if maxConsecutiveFailures has been reached | ||
func (e *exponentialBackoff) recordFailure() bool { | ||
e.mu.Lock() | ||
e.failureCount += 1 | ||
if e.backoff < e.minBackoff { | ||
e.backoff = e.minBackoff | ||
} | ||
|
||
e.backoff = min(e.backoff*2, e.maxBackoff) | ||
|
||
e.mu.Unlock() | ||
return e.failureCount >= e.maxConsecutiveFailures | ||
} | ||
|
||
// wait sleeps for the backoff duration if failureCount is non-zero. | ||
// NOTE: this is not tested and should be kept 'obviously correct' (i.e., simple) | ||
func (e *exponentialBackoff) wait() { | ||
e.mu.RLock() | ||
if e.failureCount == 0 { | ||
e.mu.RUnlock() | ||
|
||
return | ||
} | ||
e.mu.RUnlock() | ||
|
||
time.Sleep(e.backoff) | ||
} |
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,41 @@ | ||
package frankenphp | ||
|
||
import ( | ||
"github.com/stretchr/testify/assert" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestExponentialBackoff_Reset(t *testing.T) { | ||
e := &exponentialBackoff{ | ||
maxBackoff: 5 * time.Second, | ||
minBackoff: 500 * time.Millisecond, | ||
maxConsecutiveFailures: 3, | ||
} | ||
|
||
assert.False(t, e.recordFailure()) | ||
assert.False(t, e.recordFailure()) | ||
e.recordSuccess() | ||
|
||
e.mu.RLock() | ||
defer e.mu.RUnlock() | ||
assert.Equal(t, 0, e.failureCount, "expected failureCount to be reset to 0") | ||
assert.Equal(t, e.backoff, e.minBackoff, "expected backoff to be reset to minBackoff") | ||
} | ||
|
||
func TestExponentialBackoff_Trigger(t *testing.T) { | ||
e := &exponentialBackoff{ | ||
maxBackoff: 500 * 3 * time.Millisecond, | ||
minBackoff: 500 * time.Millisecond, | ||
maxConsecutiveFailures: 3, | ||
} | ||
|
||
assert.False(t, e.recordFailure()) | ||
assert.False(t, e.recordFailure()) | ||
assert.True(t, e.recordFailure()) | ||
|
||
e.mu.RLock() | ||
defer e.mu.RUnlock() | ||
assert.Equal(t, e.failureCount, e.maxConsecutiveFailures, "expected failureCount to be maxConsecutiveFailures") | ||
assert.Equal(t, e.backoff, e.maxBackoff, "expected backoff to be maxBackoff") | ||
} |
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,74 @@ | ||
package frankenphp | ||
|
||
// #include "frankenphp.h" | ||
import "C" | ||
import ( | ||
"os" | ||
"strings" | ||
"unsafe" | ||
) | ||
|
||
//export go_putenv | ||
func go_putenv(str *C.char, length C.int) C.bool { | ||
envString := C.GoStringN(str, length) | ||
|
||
// Check if '=' is present in the string | ||
if key, val, found := strings.Cut(envString, "="); found { | ||
return os.Setenv(key, val) == nil | ||
} | ||
|
||
// No '=', unset the environment variable | ||
return os.Unsetenv(envString) == nil | ||
} | ||
|
||
//export go_getfullenv | ||
func go_getfullenv(threadIndex C.uintptr_t) (*C.go_string, C.size_t) { | ||
thread := phpThreads[threadIndex] | ||
|
||
env := os.Environ() | ||
goStrings := make([]C.go_string, len(env)*2) | ||
|
||
for i, envVar := range env { | ||
key, val, _ := strings.Cut(envVar, "=") | ||
goStrings[i*2] = C.go_string{C.size_t(len(key)), thread.pinString(key)} | ||
goStrings[i*2+1] = C.go_string{C.size_t(len(val)), thread.pinString(val)} | ||
} | ||
|
||
value := unsafe.SliceData(goStrings) | ||
thread.Pin(value) | ||
|
||
return value, C.size_t(len(env)) | ||
} | ||
|
||
//export go_getenv | ||
func go_getenv(threadIndex C.uintptr_t, name *C.go_string) (C.bool, *C.go_string) { | ||
thread := phpThreads[threadIndex] | ||
|
||
// Create a byte slice from C string with a specified length | ||
envName := C.GoStringN(name.data, C.int(name.len)) | ||
|
||
// Get the environment variable value | ||
envValue, exists := os.LookupEnv(envName) | ||
if !exists { | ||
// Environment variable does not exist | ||
return false, nil // Return 0 to indicate failure | ||
} | ||
|
||
// Convert Go string to C string | ||
value := &C.go_string{C.size_t(len(envValue)), thread.pinString(envValue)} | ||
thread.Pin(value) | ||
|
||
return true, value // Return 1 to indicate success | ||
} | ||
|
||
//export go_sapi_getenv | ||
func go_sapi_getenv(threadIndex C.uintptr_t, name *C.go_string) *C.char { | ||
envName := C.GoStringN(name.data, C.int(name.len)) | ||
|
||
envValue, exists := os.LookupEnv(envName) | ||
if !exists { | ||
return nil | ||
} | ||
|
||
return phpThreads[threadIndex].pinCString(envValue) | ||
} |
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