Initial cloud-services repo - gateway service + pkg modules

This commit is contained in:
Chris Rai
2026-01-30 23:14:52 -05:00
commit fbb820d7b3
1037 changed files with 171318 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
package threadpool
// Callable the tasks which returns the output after exit should implement this interface
type Callable interface {
Call() interface{}
}
// Future is the handle returned after submitting a callable task to the thread threadpool
type Future struct {
response chan interface{}
done bool
}
// callableTask is internally used to wrap the callable and future together
// So that the worker can send the response back through channel provided in Future object
type callableTask struct {
Task Callable
Handle *Future
}
// Get returns the response of the Callable task when done
// Is is the blocking call it waits for the execution to complete
func (f *Future) Get() interface{} {
return <-f.response
}
// IsDone returns true if the execution is already done
func (f *Future) IsDone() bool {
return f.done
}
// Runnable is interface for the jobs that will be executed by the threadpool
type Runnable interface {
Run()
}

View File

@@ -0,0 +1,85 @@
package threadpool
import (
"fmt"
"sync"
)
var (
ErrQueueFull = fmt.Errorf("queue is full, not able add the task")
ErrNoWorkers = fmt.Errorf("worker pool is empty")
)
type ThreadPool struct {
workersTopLimit int
workerPool chan chan interface{}
closeHandle chan bool
wgReceivers sync.WaitGroup
}
func NewThreadPool(workersLimit int) *ThreadPool {
threadPool := &ThreadPool{workersTopLimit: workersLimit}
threadPool.workerPool = make(chan chan interface{}, workersLimit)
threadPool.closeHandle = make(chan bool)
threadPool.wgReceivers = sync.WaitGroup{}
threadPool.wgReceivers.Add(workersLimit)
threadPool.createPool()
return threadPool
}
func (t *ThreadPool) Close() {
close(t.closeHandle) // Stops all the routines
t.wgReceivers.Wait()
close(t.workerPool) // Closes the Job threadpool
}
func (t *ThreadPool) createPool() {
for i := 0; i < t.workersTopLimit; i++ {
worker := NewWorker(t.workerPool, t.closeHandle, &t.wgReceivers)
worker.Start()
}
go t.dispatch()
}
func (t *ThreadPool) submitTask(task interface{}) error {
// Add the task to the job queue
//Find a worker for the job
if t.workerPool == nil || t.workersTopLimit == 0 {
return ErrNoWorkers
}
jobChannel := <-t.workerPool
//Submit job to the worker
jobChannel <- task
return nil
}
// Execute submits the job to available worker
func (t *ThreadPool) Execute(task Runnable) error {
return t.submitTask(task)
}
// ExecuteFuture will submit the task to the threadpool and return the response handle
func (t *ThreadPool) ExecuteFuture(task Callable) (*Future, error) {
// Create future and task
if t.workerPool == nil || t.workersTopLimit == 0 {
return nil, ErrNoWorkers
}
handle := &Future{response: make(chan interface{})}
futureTask := callableTask{Task: task, Handle: handle}
err := t.submitTask(futureTask)
if err != nil {
return nil, err
}
return futureTask.Handle, nil
}
// dispatch listens to the jobqueue and handles the jobs to the workers
func (t *ThreadPool) dispatch() {
for {
select {
case <-t.closeHandle:
// Close thread threadpool
return
}
}
}

View File

@@ -0,0 +1,119 @@
package threadpool
import (
"fmt"
"testing"
"time"
)
const (
NumberOfWorkers = 20
QueueSize = int64(1000)
)
var (
threadpool *ThreadPool
)
func TestNewThreadPool(t *testing.T) {
threadpool = NewThreadPool(NumberOfWorkers)
}
func TestThreadPool_Execute(t *testing.T) {
threadpool = NewThreadPool(NumberOfWorkers)
data := &TestData{Val: "pristine"}
task := &TestTask{TestData: data}
threadpool.Execute(task)
time.Sleep(2 * time.Second)
fmt.Println("")
if data.Val != "changed" {
t.Fail()
}
}
func TestThreadPool_ExecuteFuture(t *testing.T) {
threadpool = NewThreadPool(NumberOfWorkers)
task := &TestTaskFuture{}
handle, _ := threadpool.ExecuteFuture(task)
response := handle.Get()
if !handle.IsDone() {
t.Fail()
}
fmt.Println("Thread done ", response)
}
func TestThreadPool_Close(t *testing.T) {
threadpool = NewThreadPool(NumberOfWorkers)
threadpool.Close()
}
func TestQueueFullError(t *testing.T) {
threadpool = NewThreadPool(30)
before := time.Now()
data := &TestData{Val: "pristine"}
task := &TestTask{TestData: data}
for i := 0; i < 30; i++ {
err := threadpool.Execute(task)
if err != nil {
t.Fail()
}
}
threadpool.Close()
after := time.Now()
t.Logf("time start %d", after.Sub(before))
t.Log("success")
}
// func TestQueueFullError_Future(t *testing.T) {
// threadpool = NewThreadPool(NumberOfWorkers)
// threadpool := NewThreadPool(1)
// task := &TestLongTaskFuture{}
// _, err := threadpool.ExecuteFuture(task)
// if err != nil {
// t.Fail()
// }
// _, err = threadpool.ExecuteFuture(task)
// threadpool.Close()
// }
type TestTask struct {
TestData *TestData
}
type TestData struct {
Val string
}
func (t *TestTask) Run() {
time.Sleep(1 * time.Second)
t.TestData.Val = "changed"
}
type TestLongTask struct{}
func (t TestLongTask) Run() {
time.Sleep(5 * time.Second)
}
type TestTaskFuture struct{}
func (t *TestTaskFuture) Call() interface{} {
return "Done"
}
type TestLongTaskFuture struct{}
func (t *TestLongTaskFuture) Call() interface{} {
time.Sleep(5 * time.Second)
return "Done"
}

View File

@@ -0,0 +1,60 @@
package threadpool
import (
"sync"
)
// Worker type holds the job channel and passed worker threadpool
type Worker struct {
jobChannel chan interface{}
workerPool chan chan interface{}
closeHandle chan bool
receiver *sync.WaitGroup
}
// NewWorker creates the new worker
func NewWorker(workerPool chan chan interface{}, closeHandle chan bool, waitGroup *sync.WaitGroup) *Worker {
return &Worker{workerPool: workerPool,
jobChannel: make(chan interface{}),
closeHandle: closeHandle,
receiver: waitGroup,
}
}
// Start starts the worker by listening to the job channel
func (w Worker) Start() {
go func() {
defer w.receiver.Done()
for {
// Put the worker to the worker threadpool
w.workerPool <- w.jobChannel
select {
// Wait for the job
case job := <-w.jobChannel:
// Got the job
w.executeJob(job)
case <-w.closeHandle:
// Exit the go routine when the closeHandle channel is closed
return
}
}
}()
}
// executeJob executes the job based on the type
func (w Worker) executeJob(job interface{}) {
// Execute the job based on the task type
switch task := job.(type) {
case Runnable:
task.Run()
break
case callableTask:
response := task.Task.Call()
task.Handle.done = true
task.Handle.response <- response
break
}
}