zig-pay/src/services/service_pool.zig
2025-08-07 22:07:35 -03:00

215 lines
6.3 KiB
Zig

const std = @import("std");
const testing = std.testing;
const expect = testing.expect;
const HttpService = @import("http_service.zig").HttpService;
const s = @import("service.zig");
const ServiceTicket = s.ServiceTicket;
const ServicePoolError = error{ FullPoll, NoServiceAvailable };
pub const ServicePool = struct {
services: [10]*HttpService = undefined,
services_len: usize = 0,
pub fn add(self: *ServicePool, service: *HttpService) ServicePoolError!void {
if (service.connections.len == 0) {
return;
}
if (self.services_len == self.services.len) {
return ServicePoolError.FullPoll;
}
self.services[self.services_len] = service;
self.services_len += 1;
}
pub fn dive(self: *ServicePool, message: []const u8) ServicePoolError!*ServiceTicket {
var skip: [10]bool = .{false} ** 10;
for (0..self.services_len) |_| {
var best_service = self.findBestService(&skip);
if (best_service == null) {
return error.NoServiceAvailable;
}
const ticket = best_service.?.queue(message);
if (ticket == null) {
continue;
}
return ticket.?;
}
return error.NoServiceAvailable;
}
fn findBestService(self: *ServicePool, skip: []bool) ?*HttpService {
var first_service_available_i: ?usize = null;
var best_perfomance_i: ?usize = null;
for (0..self.services_len) |i| {
if (skip[i]) {
continue;
}
const service = self.services[i];
if (service.health == .unavailable or service.capacity >= 90) {
continue;
}
if (first_service_available_i == null) {
first_service_available_i = i;
}
if (best_perfomance_i == null or self.services[best_perfomance_i.?].response_time > service.response_time) {
best_perfomance_i = i;
}
}
if (best_perfomance_i != null) {
skip[best_perfomance_i.?] = true;
return self.services[best_perfomance_i.?];
}
if (first_service_available_i != null) {
skip[first_service_available_i.?] = true;
return self.services[first_service_available_i.?];
}
return null;
}
};
test "expect error FullPoll if more then 10 service is added" {
var service_pool = ServicePool{};
for (0..10) |_| {
var service = try HttpService.init(1, "default", "one.one.one.one", 53, 40, testing.allocator);
defer service.deinit();
try service_pool.add(&service);
}
var service = try HttpService.init(1, "default", "one.one.one.one", 53, 40, testing.allocator);
defer service.deinit();
service_pool.add(&service) catch |err| {
try expect(err == ServicePoolError.FullPoll);
return;
};
try expect(false);
}
test "expect NoServiceAvailable if no service has been add" {
var service_pool = ServicePool{};
_ = service_pool.dive("test") catch |err| {
try expect(err == ServicePoolError.NoServiceAvailable);
return;
};
try expect(false);
}
test "expect if NoServiceAvailable all sevice are unavailable" {
var service_pool = ServicePool{};
for (0..10) |_| {
var service = try HttpService.init(1, "default", "one.one.one.one", 53, 40, testing.allocator);
service.health = .unavailable;
defer service.deinit();
try service_pool.add(&service);
}
var service = try HttpService.init(2, "default", "one.one.one.one", 53, 40, testing.allocator);
defer service.deinit();
service_pool.add(&service) catch |err| {
try expect(err == ServicePoolError.FullPoll);
return;
};
try expect(false);
}
test "expect choice service with best perfomance" {
var service_pool = ServicePool{};
var service1 = try HttpService.init(1, "default", "one.one.one.one", 53, 40, testing.allocator);
service1.health = .available;
service1.response_time = 100;
defer service1.deinit();
try service_pool.add(&service1);
var service2 = try HttpService.init(2, "fallback", "one.one.one.one", 53, 40, testing.allocator);
service2.health = .available;
service2.response_time = 10;
defer service2.deinit();
try service_pool.add(&service2);
var skip: [10]bool = .{false} ** 10;
const res = service_pool.findBestService(&skip);
try expect(res.? == &service2);
try expect(skip[1]);
}
test "expect choice service first service available if all has the same perfomance" {
var service_pool = ServicePool{};
var service1 = try HttpService.init(1, "default", "one.one.one.one", 53, 40, testing.allocator);
service1.health = .available;
service1.response_time = 10;
defer service1.deinit();
try service_pool.add(&service1);
var service2 = try HttpService.init(2, "fallback", "one.one.one.one", 53, 40, testing.allocator);
service2.health = .available;
service2.response_time = 10;
defer service2.deinit();
try service_pool.add(&service2);
var skip: [10]bool = .{false} ** 10;
const res = service_pool.findBestService(&skip);
try expect(res.? == &service1);
try expect(skip[0]);
}
test "expect choice service with overloaded" {
var service_pool = ServicePool{};
var service1 = try HttpService.init(1, "default", "one.one.one.one", 53, 40, testing.allocator);
service1.health = .available;
service1.response_time = 10;
service1.capacity = 90;
defer service1.deinit();
try service_pool.add(&service1);
var service2 = try HttpService.init(2, "fallback", "one.one.one.one", 53, 40, testing.allocator);
service2.health = .available;
service2.response_time = 10;
service2.capacity = 100;
defer service2.deinit();
try service_pool.add(&service2);
var service3 = try HttpService.init(3, "fallback_fallback", "one.one.one.one", 53, 40, testing.allocator);
service3.health = .available;
service3.response_time = 10;
service3.capacity = 50;
defer service3.deinit();
try service_pool.add(&service3);
var skip: [10]bool = .{false} ** 10;
const res = service_pool.findBestService(&skip);
try expect(res.? == &service3);
try expect(skip[2]);
}