From 5f170c46491f13d35d39f1dce47227631448a326 Mon Sep 17 00:00:00 2001 From: Bharat Kathi Date: Tue, 27 Aug 2024 14:42:25 -0700 Subject: [PATCH 1/9] some inital routing docs --- README.md | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8eafb4b..7a815d1 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,9 @@ The `id` field is an auto-generated integer with the length specified by the `SE ### Example Say we register a service `New York` with the following definition: + +`[POST] http://localhost:10311/rincon/services` + ```json { "name": "New York", @@ -150,10 +153,97 @@ Any `2xx` status code will mark the heartbeat as successful. Any other response ### Client Heartbeats -When operating in client heartbeat mode, Rincon expects pings from the registered services at least once every heartbeat interval. This ping should be made to the service registration endpoint, with all the service fields correctly populated. The ping can be cofirmed by ensuring the `updated_at` field in the response has increased. At each heartbeat interval, Rincon will remove all services with an updated_at timestamp older than the interval. +When operating in client heartbeat mode, Rincon expects pings from the registered services at least once every heartbeat interval. This ping should be made to the service registration endpoint, with all the service fields correctly populated. The ping can be cofirmed by ensuring the `updated_at` field in the response has increased. At each heartbeat interval, Rincon will remove all services with an `updated_at` timestamp older than the interval. ## Routing +Services register routes to tell Rincon what requests they can handle. Each route has the following properties: + +- `id`: The unique identifier for the route. +- `route`: The actual route path. +- `method`: The http methods associated with the route. +- `service_name`: The service associated with the route. + +When registering a route, you must provide the route, method, and service associated with that route. Upon registration, Rincon will return the following Route object. + +```json +{ + "id": "/rincon/ping-[*]", + "route": "/rincon/ping", + "method": "*", + "service_name": "rincon", + "created_at": "2024-08-04T01:05:37.262645179-07:00" +} +``` + +The `id` is a generated field in the format `route-[method]`. Note that routes are tied to services, not any specific instance of a service. So if we register 10 routes to the `new_york` service and then spin up 2 more instances of the `new_york` service, all instances of the `new_york` service are considered able to handle those 10 routes. + +### Example + +Using our `New York` service from the previous example, let's register the following route. + +`[POST] http://localhost:10311/rincon/routes` + +```json +{ + "route": "/users", + "method": "*", + "service_name": "New York", + } +``` + +Rincon will return the following route object to us. + +```json +{ + "id": "/users-[*]", + "route": "/users", + "method": "*", + "service_name": "new_york", + "created_at": "2024-08-27T14:04:43.688527-07:00" +} +``` + +Now we can confirm our route was correctly registered by making a request to the route matching endpoint. + +`[GET] http://localhost:10311/rincon/match?route=users&method=GET` + +```json +{ + "id": 416156, + "name": "new_york", + "version": "1.0.0", + "endpoint": "http://localhost:3000", + "health_check": "http://localhost:3000/health", + "updated_at": "2024-08-27T14:04:23.172214-07:00", + "created_at": "2024-08-27T14:04:23.172214-07:00" +} +``` + +As expected, Rincon returned our `New York` service definition. Now let's try to register a route for a different service (assume that `San Francisco` is a service that has already been registered with Rincon). + +`[POST] http://localhost:10311/rincon/routes` + +```json +{ + "route": "/users", + "method": "*", + "service_name": "San Francisco", + } +``` + +Rincon will return the following route object to us. + +```json +{ + "id": "/users-[*]", + "route": "/users", + "method": "*", + "service_name": "new_york", + "created_at": "2024-08-27T14:04:43.688527-07:00" +} +``` + ## Load Balancing ## Configuration From ad8c7e3a03ec462f7c00ad4a30efc664d27632a8 Mon Sep 17 00:00:00 2001 From: Bharat Kathi Date: Tue, 27 Aug 2024 15:28:45 -0700 Subject: [PATCH 2/9] add route stacking, conflicts, and overwriting docs --- README.md | 104 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7a815d1..2f411ac 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,96 @@ When registering a route, you must provide the route, method, and service associ The `id` is a generated field in the format `route-[method]`. Note that routes are tied to services, not any specific instance of a service. So if we register 10 routes to the `new_york` service and then spin up 2 more instances of the `new_york` service, all instances of the `new_york` service are considered able to handle those 10 routes. +### Supported Methods + +Rincon currently supports the following HTTP Methods: +- `GET` +- `POST` +- `PUT` +- `DELETE` +- `PATCH` +- `OPTIONS` +- `HEAD` +- `*` (wilcard, route can handle all methods) + +### Stacking Routes + +Routes with the same path and service name will automatically be "stacked". This just means that their methods will be combined into one registration in Rincon. + +```c +New York: /users [GET] +New York: /users [POST] +--- +New York: /users [GET,POST] +``` + +If the existing or new route method is `*`, then the stacked method will simply be the wildcard method. + +```c +New York: /users [*] +New York: /users [POST] +--- +New York: /users [*] +``` + +### Conflicting Route Registrations + +By default, `OVERWRITE_ROUTES` is set to `false`. This means that if you attempt to register a route that has a conflict with an existing route, it will be rejected. Usually these conflicts arise from two routes attached to different services having an overlap in their methods. + +```c +New York: /users [GET] +San Francisco: /users [GET] # cannot be registered +``` + +Even if the newer route has a higher method coverage than the existing route, the registration will be rejected as long as `OVERWRITE_ROUTES` is set to `false`. + +```c +New York: /users [GET] +San Francisco: /users [GET,POST] # cannot be registered +``` + +To ensure that your routes are registered successfully, make sure there are no method overlaps. + +```c +New York: /users [GET] +San Francisco: /users [POST,PUT] # no conflict, will be registered successfully! +``` + +### Overwriting Routes + +When `OVERWRITE_ROUTES` is set to `true`, any conflicting registrations will not be rejected. Instead the new registration will replace the existing one. + +```c +New York: /users [GET] +San Francisco: /users [GET] +--- +San Francisco: /users [GET] +``` + +If there are multiple conflicting routes, they will all be replaced. + +```c +New York: /users [GET] +Boston: /users [POST] +Los Angelos /users [DELETE] +San Francisco: /users [GET,POST,DELETE] +--- +San Francisco: /users [GET,POST,DELETE] +``` + +> [!WARNING] +> Existing routes will be replaced even if they have a higher route coverage than the new route. Be careful when overwriting routes! +> ```c +> New York: /users [GET,POST] +> San Francisco: /users [GET] +> --- +> San Francisco: /users [GET] +> ``` + +### Route Matching + + + ### Example Using our `New York` service from the previous example, let's register the following route. @@ -227,23 +317,21 @@ As expected, Rincon returned our `New York` service definition. Now let's try to ```json { "route": "/users", - "method": "*", + "method": "POST", "service_name": "San Francisco", - } +} ``` -Rincon will return the following route object to us. +This time, we get the following error from Rincon. ```json { - "id": "/users-[*]", - "route": "/users", - "method": "*", - "service_name": "new_york", - "created_at": "2024-08-27T14:04:43.688527-07:00" + "message": "route with id /users-[POST] overlaps with existing routes [[*] /users (new_york)]" } ``` +This is because `New York` was already registered to handle all methods (based on the `*` wildcard method definition) on the `/users` route. By default a new service cannot register a route with a conflicting method. This can be changed by setting `OVERWRITE_ROUTES`. + ## Load Balancing ## Configuration From 0556d55479589e036c14eba2e0c83f519ab7178c Mon Sep 17 00:00:00 2001 From: Bharat Kathi Date: Tue, 27 Aug 2024 15:31:34 -0700 Subject: [PATCH 3/9] update warning to caution --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2f411ac..5f42390 100644 --- a/README.md +++ b/README.md @@ -255,13 +255,13 @@ San Francisco: /users [GET,POST,DELETE] San Francisco: /users [GET,POST,DELETE] ``` -> [!WARNING] +> [!CAUTION] > Existing routes will be replaced even if they have a higher route coverage than the new route. Be careful when overwriting routes! > ```c > New York: /users [GET,POST] > San Francisco: /users [GET] > --- -> San Francisco: /users [GET] +> San Francisco: /users [GET] # route for New York was completely removed! > ``` ### Route Matching From a6206db11168fcd0608ecc7800b57a082c9eb117 Mon Sep 17 00:00:00 2001 From: Bharat Kathi Date: Tue, 27 Aug 2024 15:37:54 -0700 Subject: [PATCH 4/9] route example code formatting --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5f42390..ba826fd 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ Rincon currently supports the following HTTP Methods: Routes with the same path and service name will automatically be "stacked". This just means that their methods will be combined into one registration in Rincon. -```c +``` New York: /users [GET] New York: /users [POST] --- @@ -203,7 +203,7 @@ New York: /users [GET,POST] If the existing or new route method is `*`, then the stacked method will simply be the wildcard method. -```c +``` New York: /users [*] New York: /users [POST] --- @@ -214,21 +214,21 @@ New York: /users [*] By default, `OVERWRITE_ROUTES` is set to `false`. This means that if you attempt to register a route that has a conflict with an existing route, it will be rejected. Usually these conflicts arise from two routes attached to different services having an overlap in their methods. -```c +```cpp New York: /users [GET] -San Francisco: /users [GET] # cannot be registered +San Francisco: /users [GET] // cannot be registered ``` Even if the newer route has a higher method coverage than the existing route, the registration will be rejected as long as `OVERWRITE_ROUTES` is set to `false`. -```c +``` New York: /users [GET] San Francisco: /users [GET,POST] # cannot be registered ``` To ensure that your routes are registered successfully, make sure there are no method overlaps. -```c +``` New York: /users [GET] San Francisco: /users [POST,PUT] # no conflict, will be registered successfully! ``` @@ -237,7 +237,7 @@ San Francisco: /users [POST,PUT] # no conflict, will be registered successfully! When `OVERWRITE_ROUTES` is set to `true`, any conflicting registrations will not be rejected. Instead the new registration will replace the existing one. -```c +``` New York: /users [GET] San Francisco: /users [GET] --- @@ -246,7 +246,7 @@ San Francisco: /users [GET] If there are multiple conflicting routes, they will all be replaced. -```c +``` New York: /users [GET] Boston: /users [POST] Los Angelos /users [DELETE] From 0f2e5ef45fed46d8f96c18ebbdc157b00a2fa277 Mon Sep 17 00:00:00 2001 From: Bharat Kathi Date: Tue, 27 Aug 2024 15:38:20 -0700 Subject: [PATCH 5/9] route example code formatting --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ba826fd..02f7f57 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ Rincon currently supports the following HTTP Methods: Routes with the same path and service name will automatically be "stacked". This just means that their methods will be combined into one registration in Rincon. -``` +```c New York: /users [GET] New York: /users [POST] --- @@ -203,7 +203,7 @@ New York: /users [GET,POST] If the existing or new route method is `*`, then the stacked method will simply be the wildcard method. -``` +```c New York: /users [*] New York: /users [POST] --- @@ -214,30 +214,30 @@ New York: /users [*] By default, `OVERWRITE_ROUTES` is set to `false`. This means that if you attempt to register a route that has a conflict with an existing route, it will be rejected. Usually these conflicts arise from two routes attached to different services having an overlap in their methods. -```cpp +```c New York: /users [GET] San Francisco: /users [GET] // cannot be registered ``` Even if the newer route has a higher method coverage than the existing route, the registration will be rejected as long as `OVERWRITE_ROUTES` is set to `false`. -``` +```c New York: /users [GET] -San Francisco: /users [GET,POST] # cannot be registered +San Francisco: /users [GET,POST] // cannot be registered ``` To ensure that your routes are registered successfully, make sure there are no method overlaps. -``` +```c New York: /users [GET] -San Francisco: /users [POST,PUT] # no conflict, will be registered successfully! +San Francisco: /users [POST,PUT] // no conflict, will be registered successfully! ``` ### Overwriting Routes When `OVERWRITE_ROUTES` is set to `true`, any conflicting registrations will not be rejected. Instead the new registration will replace the existing one. -``` +```c New York: /users [GET] San Francisco: /users [GET] --- @@ -246,7 +246,7 @@ San Francisco: /users [GET] If there are multiple conflicting routes, they will all be replaced. -``` +```c New York: /users [GET] Boston: /users [POST] Los Angelos /users [DELETE] @@ -261,7 +261,7 @@ San Francisco: /users [GET,POST,DELETE] > New York: /users [GET,POST] > San Francisco: /users [GET] > --- -> San Francisco: /users [GET] # route for New York was completely removed! +> San Francisco: /users [GET] // route for New York was completely removed! > ``` ### Route Matching From 61353ce6859ab15aaf378e3dd97b739c46b465b0 Mon Sep 17 00:00:00 2001 From: Bharat Kathi Date: Tue, 27 Aug 2024 16:31:55 -0700 Subject: [PATCH 6/9] add wildcard routing to docs --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/README.md b/README.md index 02f7f57..209ec19 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,60 @@ Rincon currently supports the following HTTP Methods: - `HEAD` - `*` (wilcard, route can handle all methods) +### Wildcard Routes + +Dynamic routing is supported through the use of wildcards, enabling services to handle variable path segments efficiently. + +#### `/*` - Any Wildcard + +This wildcard can be used to allow any string to match to a certain path segment. + +``` +/users/* +--- +/users/123 +/users/abc +``` + +You can also use this wildcard in the middle of your route. + +``` +/users/*/profile +--- +/users/123/profile +/users/abc/profile +``` + +#### `/**` - All Wildcard + +This wildcard can be used to allow any string to match to all remaining path segments. + +``` +/users/** +--- +/users/123 +/users/abc/edit +/users/a1b2c3/account/settings +``` + +You can even use both wildcards for more specific routing. + +``` +/users/*/profile/** +--- +/users/123/profile/edit +/users/abc/profile/settings/notifications +``` + +> [!CAUTION] +> While you can have the all wildcard (`**`) in the middle of a route path, when the route graph is computed all proceeding segments are ignored. +> ``` +> /users/**/profile +> --- +> /users/123/profile/edit +> /users/abc/profile/settings/notifications +> ``` + ### Stacking Routes Routes with the same path and service name will automatically be "stacked". This just means that their methods will be combined into one registration in Rincon. From 5dbca062eb5e5efb1f310c2f6eff37888d206d24 Mon Sep 17 00:00:00 2001 From: Bharat Kathi Date: Tue, 27 Aug 2024 16:32:42 -0700 Subject: [PATCH 7/9] change caution to warning --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 209ec19..7d28b20 100644 --- a/README.md +++ b/README.md @@ -235,13 +235,14 @@ You can even use both wildcards for more specific routing. /users/abc/profile/settings/notifications ``` -> [!CAUTION] +> [!WARNING] > While you can have the all wildcard (`**`) in the middle of a route path, when the route graph is computed all proceeding segments are ignored. > ``` > /users/**/profile > --- -> /users/123/profile/edit -> /users/abc/profile/settings/notifications +> /users/123 +> /users/abc/profile +> /users/a1b2c3/profile/edit > ``` ### Stacking Routes From aaef29b4b211b5714c67ab9f46db66aecbebed0f Mon Sep 17 00:00:00 2001 From: Bharat Kathi Date: Tue, 27 Aug 2024 16:40:40 -0700 Subject: [PATCH 8/9] begin route matching docs --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 7d28b20..9875a47 100644 --- a/README.md +++ b/README.md @@ -321,7 +321,15 @@ San Francisco: /users [GET,POST,DELETE] ### Route Matching +Internally, Rincon computes a route graph to help it match a requested route against its registered routes. This is a simple directed acyclic graph where nodes are route paths and edges are slugs needed to get to the next route path. Nodes also contain information about which services and methods can be handled at that route path. +As an example, let's say we have the following route registrations. + +``` +New York: /users +New York: /users/* +San Francsico: /users/*/notifications +``` ### Example From 8244d2edebbf3e7e4f1806355973a1a205369c03 Mon Sep 17 00:00:00 2001 From: Bharat Kathi Date: Tue, 27 Aug 2024 22:12:07 -0700 Subject: [PATCH 9/9] default overwrite routes to false --- utils/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/config.go b/utils/config.go index 622bc43..84d1ae0 100644 --- a/utils/config.go +++ b/utils/config.go @@ -42,8 +42,8 @@ func VerifyConfig() { SugarLogger.Infoln("STORAGE_MODE is not set, defaulting to local") } if config.OverwriteRoutes == "" { - config.OverwriteRoutes = "true" - SugarLogger.Debugln("OVERWRITE_ROUTES is not set, defaulting to true") + config.OverwriteRoutes = "false" + SugarLogger.Debugln("OVERWRITE_ROUTES is not set, defaulting to false") } if config.HeartbeatType == "" { config.HeartbeatType = "server"