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]); }