kingluo commented on code in PR #9692:
URL: https://github.com/apache/apisix/pull/9692#discussion_r1252573136


##########
apisix/http/router/radixtree_uri.lua:
##########
@@ -18,24 +18,128 @@ local require = require
 local core = require("apisix.core")
 local base_router = require("apisix.http.route")
 local get_services = require("apisix.http.service").services
+local apisix_router = require("apisix.router")
+local json = require("apisix.core.json")
+local table = require("apisix.core.table")
 local cached_router_version
 local cached_service_version
+local uri_routes = {}
+local uri_router
+local match_opts = {}
 
 
 local _M = {version = 0.2}
 
 
-    local uri_routes = {}
-    local uri_router
-    local match_opts = {}
+local function incremental_operate_radixtree(routes)
+    local sync_tb = apisix_router.sync_tb
+    if apisix_router.need_create_radixtree then
+        uri_router = base_router.create_radixtree_uri_router(routes, 
uri_routes, false)
+        apisix_router.need_create_radixtree = false
+        for k, _ in pairs(sync_tb) do
+            sync_tb[k] = nil
+        end
+        return
+    end
+
+    local op, route, last_route, err
+    local cur_tmp, last_tmp = {}, {}
+    local router_opts = {
+        no_param_match = true
+    }
+    for k, v in pairs(sync_tb) do
+        op = sync_tb[k]["op"]
+        route = sync_tb[k]["cur_route"]
+        last_route = sync_tb[k]["last_route"]
+        cur_tmp = {}
+        last_tmp = {}
+
+        if route and route.value then
+            local status = table.try_read_attr(route, "value", "status")
+            if status and status == 0 then
+                return
+            end
+
+            local filter_fun, err
+            if route.value.filter_func then
+                filter_fun, err = loadstring(
+                    "return " .. route.value.filter_func,
+                    "router#" .. route.value.id
+                )
+                if not filter_fun then
+                    core.log.error("failed to load filter function: ", err, " 
route id", route.value.id)
+                    return
+                end
+
+                filter_fun = filter_fun()
+            end
+
+            cur_tmp = {
+                id = route.value.id,
+                paths = route.value.uris or route.value.uri,
+                methods = route.value.methods,
+                priority = route.value.priority,
+                hosts = route.value.hosts or route.value.host,
+                remote_addrs = route.value.remote_addrs or 
route.value.remote_addr,
+                vars = route.value.vars,
+                filter_fun = filter_fun,
+                handler = function(api_ctx, match_opts)
+                    api_ctx.matched_params = nil
+                    api_ctx.matched_route = route
+                    api_ctx.curr_req_matched = match_opts.matched
+                end
+            }
+        end
+
+        if last_route and last_route.value then
+            last_tmp = {
+                id = last_route.value.id,
+                paths = last_route.value.uris or last_route.value.uri,
+                methods = last_route.value.methods,
+                priority = last_route.value.priority,
+                hosts = last_route.value.hosts or last_route.value.host,
+                remote_addrs = last_route.value.remote_addrs or 
last_route.value.remote_addr,
+                vars = last_route.value.vars
+            }
+        end
+
+        if op == "update" then
+            core.log.notice("update routes watched from etcd into radixtree.", 
json.encode(route))
+            err = uri_router:update_route(last_tmp, cur_tmp, router_opts)
+            if err ~= nil then
+                core.log.error("update a route into radixtree failed.", 
json.encode(route), err)
+                return
+            end
+        elseif op == "create" then
+            core.log.notice("create routes watched from etcd into radixtree.", 
json.encode(route))
+            err = uri_router:add_route(cur_tmp, router_opts)
+            if err ~= nil then
+                core.log.error("add routes into radixtree failed.", 
json.encode(route), err)
+                return
+            end
+        elseif op == "delete" then
+            core.log.notice("delete routes watched from etcd into radixtree.", 
json.encode(last_route))
+            err = uri_router:delete_route(last_tmp, router_opts)
+            if err ~= nil then
+                core.log.error("delete a route into radixtree failed.", 
json.encode(last_route), err)
+                return
+            end
+        end
+
+        sync_tb[k] = nil
+    end
+
+    apisix_router.sync_tb = sync_tb

Review Comment:
   no need to reassign the table? because they point to the same table.



##########
apisix/http/router/radixtree_uri.lua:
##########
@@ -18,24 +18,128 @@ local require = require
 local core = require("apisix.core")
 local base_router = require("apisix.http.route")
 local get_services = require("apisix.http.service").services
+local apisix_router = require("apisix.router")
+local json = require("apisix.core.json")
+local table = require("apisix.core.table")
 local cached_router_version
 local cached_service_version
+local uri_routes = {}
+local uri_router
+local match_opts = {}
 
 
 local _M = {version = 0.2}
 
 
-    local uri_routes = {}
-    local uri_router
-    local match_opts = {}
+local function incremental_operate_radixtree(routes)
+    local sync_tb = apisix_router.sync_tb
+    if apisix_router.need_create_radixtree then
+        uri_router = base_router.create_radixtree_uri_router(routes, 
uri_routes, false)
+        apisix_router.need_create_radixtree = false
+        for k, _ in pairs(sync_tb) do
+            sync_tb[k] = nil
+        end
+        return
+    end
+
+    local op, route, last_route, err
+    local cur_tmp, last_tmp = {}, {}
+    local router_opts = {
+        no_param_match = true
+    }
+    for k, v in pairs(sync_tb) do
+        op = sync_tb[k]["op"]
+        route = sync_tb[k]["cur_route"]
+        last_route = sync_tb[k]["last_route"]
+        cur_tmp = {}
+        last_tmp = {}
+
+        if route and route.value then
+            local status = table.try_read_attr(route, "value", "status")
+            if status and status == 0 then
+                return
+            end
+
+            local filter_fun, err
+            if route.value.filter_func then
+                filter_fun, err = loadstring(
+                    "return " .. route.value.filter_func,
+                    "router#" .. route.value.id
+                )
+                if not filter_fun then
+                    core.log.error("failed to load filter function: ", err, " 
route id", route.value.id)
+                    return
+                end
+
+                filter_fun = filter_fun()
+            end
+
+            cur_tmp = {
+                id = route.value.id,
+                paths = route.value.uris or route.value.uri,
+                methods = route.value.methods,
+                priority = route.value.priority,
+                hosts = route.value.hosts or route.value.host,
+                remote_addrs = route.value.remote_addrs or 
route.value.remote_addr,
+                vars = route.value.vars,
+                filter_fun = filter_fun,
+                handler = function(api_ctx, match_opts)
+                    api_ctx.matched_params = nil
+                    api_ctx.matched_route = route
+                    api_ctx.curr_req_matched = match_opts.matched
+                end
+            }
+        end
+
+        if last_route and last_route.value then
+            last_tmp = {
+                id = last_route.value.id,
+                paths = last_route.value.uris or last_route.value.uri,
+                methods = last_route.value.methods,
+                priority = last_route.value.priority,
+                hosts = last_route.value.hosts or last_route.value.host,
+                remote_addrs = last_route.value.remote_addrs or 
last_route.value.remote_addr,
+                vars = last_route.value.vars
+            }
+        end
+
+        if op == "update" then
+            core.log.notice("update routes watched from etcd into radixtree.", 
json.encode(route))
+            err = uri_router:update_route(last_tmp, cur_tmp, router_opts)
+            if err ~= nil then
+                core.log.error("update a route into radixtree failed.", 
json.encode(route), err)
+                return
+            end
+        elseif op == "create" then
+            core.log.notice("create routes watched from etcd into radixtree.", 
json.encode(route))
+            err = uri_router:add_route(cur_tmp, router_opts)
+            if err ~= nil then
+                core.log.error("add routes into radixtree failed.", 
json.encode(route), err)
+                return
+            end
+        elseif op == "delete" then
+            core.log.notice("delete routes watched from etcd into radixtree.", 
json.encode(last_route))
+            err = uri_router:delete_route(last_tmp, router_opts)
+            if err ~= nil then
+                core.log.error("delete a route into radixtree failed.", 
json.encode(last_route), err)
+                return
+            end
+        end
+
+        sync_tb[k] = nil

Review Comment:
   clear the whole table after the loop?



##########
apisix/http/router/radixtree_host_uri.lua:
##########
@@ -29,81 +32,246 @@ local cached_router_version
 local cached_service_version
 local host_router
 local only_uri_router
+local host_routes = {}
+local only_uri_routes = {}
 
 
 local _M = {version = 0.1}
 
 
-local function push_host_router(route, host_routes, only_uri_routes)
-    if type(route) ~= "table" then
+local function empty_func() end
+
+
+local function push_host_router(route, host_routes, only_uri_routes, 
all_hosts, op, rdx_rt, pre_route, pre_rdx_rt)
+    if route and type(route) ~= "table" then
         return
     end
 
-    local filter_fun, err
-    if route.value.filter_func then
-        filter_fun, err = loadstring(
-                                "return " .. route.value.filter_func,
-                                "router#" .. route.value.id)
-        if not filter_fun then
-            core.log.error("failed to load filter function: ", err,
-                            " route id: ", route.value.id)
-            return
+    local radixtree_route, pre_radixtree_route = {}, {}
+    local hosts
+    if route and route.value then
+        hosts = route.value.hosts
+        if not hosts then
+            if route.value.host then
+                hosts = {route.value.host}
+            elseif route.value.service_id then
+                local service = service_fetch(route.value.service_id)
+                if not service then
+                    core.log.error("failed to fetch service configuration by ",
+                                    "id: ", route.value.service_id)
+                    -- we keep the behavior that missing service won't affect 
the route matching
+                else
+                    hosts = service.value.hosts
+                end
+            end
+        end
+
+        local filter_fun, err
+        if route.value and route.value.filter_func then
+            filter_fun, err = loadstring(
+                                    "return " .. route.value.filter_func,
+                                    "router#" .. route.value.id)
+            if not filter_fun then
+                core.log.error("failed to load filter function: ", err,
+                                " route id: ", route.value.id)
+                return
+            end
+
+            filter_fun = filter_fun()
+        end
+
+        radixtree_route = {
+            id = route.value.id,
+            paths = route.value.uris or route.value.uri,
+            methods = route.value.methods,
+            priority = route.value.priority,
+            remote_addrs = route.value.remote_addrs
+                        or route.value.remote_addr,
+            vars = route.value.vars,
+            filter_fun = filter_fun,
+            handler = function (api_ctx, match_opts)
+                api_ctx.matched_params = nil
+                api_ctx.matched_route = route
+                api_ctx.curr_req_matched = match_opts.matched
+                api_ctx.real_curr_req_matched_path = match_opts.matched._path
+            end
+        }
+
+        if rdx_rt ~= nil then
+            for k, v in pairs(radixtree_route) do
+                rdx_rt[k] = v
+            end
         end
+    end
 
-        filter_fun = filter_fun()
+    if hosts == nil and all_hosts == nil then
+        core.table.insert(only_uri_routes, radixtree_route)
+        return
     end
 
-    local hosts = route.value.hosts
-    if not hosts then
-        if route.value.host then
-            hosts = {route.value.host}
-        elseif route.value.service_id then
-            local service = service_fetch(route.value.service_id)
-            if not service then
-                core.log.error("failed to fetch service configuration by ",
-                                "id: ", route.value.service_id)
-                -- we keep the behavior that missing service won't affect the 
route matching
-            else
-                hosts = service.value.hosts
+    local pre_hosts
+    if pre_route and pre_route.value then
+        pre_hosts = pre_route.value.hosts
+        if not pre_hosts then
+            if pre_route.value.host then
+                pre_hosts = {pre_route.value.host}
+            elseif pre_route.value.service_id then
+                local service = service_fetch(pre_route.value.service_id)
+                if not service then
+                    core.log.error("failed to fetch service configuration by ",
+                                    "id: ", pre_route.value.service_id)
+                    -- we keep the behavior that missing service won't affect 
the route matching
+                else
+                    pre_hosts = service.value.hosts
+                end
+            end
+        end
+
+        local filter_fun, err
+        if pre_route.value and pre_route.value.filter_func then
+            filter_fun, err = loadstring(
+                                    "return " .. pre_route.value.filter_func,
+                                    "router#" .. pre_route.value.id)
+            if not filter_fun then
+                core.log.error("failed to load filter function: ", err,
+                                " route id: ", pre_route.value.id)
+                return
+            end
+
+            filter_fun = filter_fun()
+        end
+
+        pre_radixtree_route = {
+            id = pre_route.value.id,
+            paths = pre_route.value.uris or pre_route.value.uri,
+            methods = pre_route.value.methods,
+            priority = pre_route.value.priority,
+            remote_addrs = pre_route.value.remote_addrs
+                           or pre_route.value.remote_addr,
+            vars = pre_route.value.vars,
+            filter_fun = filter_fun,
+            handler = function (api_ctx, match_opts)
+                api_ctx.matched_params = nil
+                api_ctx.matched_route = pre_route
+                api_ctx.curr_req_matched = match_opts.matched
+                api_ctx.real_curr_req_matched_path = match_opts.matched._path
+            end
+        }
+
+        if pre_rdx_rt ~= nil then
+            for k, v in pairs(pre_radixtree_route) do
+                pre_rdx_rt[k] = v
             end
         end
     end
 
-    local radixtree_route = {
-        paths = route.value.uris or route.value.uri,
-        methods = route.value.methods,
-        priority = route.value.priority,
-        remote_addrs = route.value.remote_addrs
-                       or route.value.remote_addr,
-        vars = route.value.vars,
-        filter_fun = filter_fun,
-        handler = function (api_ctx, match_opts)
-            api_ctx.matched_params = nil
-            api_ctx.matched_route = route
-            api_ctx.curr_req_matched = match_opts.matched
-            api_ctx.real_curr_req_matched_path = match_opts.matched._path
+    if all_hosts ~= nil then
+        all_hosts["host"] = hosts
+        all_hosts["pre_host"] = pre_hosts
+    end
+
+    local pre_t = {}
+    if pre_hosts then
+        for i, h in ipairs(pre_hosts) do
+            local rev_h = h:reverse()
+            pre_t[rev_h] = 1
         end
-    }
+    end
 
-    if hosts == nil then
-        core.table.insert(only_uri_routes, radixtree_route)
-        return
+    local t = {}
+    if hosts then
+        for i, h in ipairs(hosts) do
+            local rev_h = h:reverse()
+            t[rev_h] = 1
+        end
+    end
+
+    local comm = {}
+    for k, v in pairs(pre_t) do
+        if t[k] ~= nil then
+            tab_insert(comm, k)
+            pre_t[k] = nil
+            t[k] = nil
+        end
+    end
+
+    for _, j in ipairs(comm) do
+        local routes = host_routes[j]
+        if routes == nil then
+            core.log.error("no routes array for reverse host in the map.", j)
+            return
+        end
+
+        local found = false
+        for i, r in ipairs(routes) do
+            if r.id == radixtree_route.id then
+                routes[i] = radixtree_route
+                found = true
+                if op then
+                    table.insert(op["upd"], j)
+                end
+                break
+            end
+        end
+
+        if not found then
+            core.log.error("cannot find the route in common host's table.", j, 
radixtree_route.id)
+            return
+        end
     end
 
-    for i, host in ipairs(hosts) do
-        local host_rev = host:reverse()
-        if not host_routes[host_rev] then
-            host_routes[host_rev] = {radixtree_route}
+    for k, v in pairs(pre_t) do
+        local routes = host_routes[k]
+        if routes == nil then
+            core.log.error("no routes array for reverse host in the map.", k)
+            return
+        end
+
+        local found = false
+        for i, r in ipairs(routes) do
+            if r.id == pre_radixtree_route.id then
+                table.remove(routes, i)
+                found = true
+                break
+            end
+        end
+
+        if not found then
+            core.log.error("cannot find the route in previous host's table.", 
k, pre_radixtree_route.id)
+            return
+        end
+
+        if #routes == 0 then
+            host_routes[k] = nil
+            if op then
+                core.log.error("###################del####", k)

Review Comment:
   remove the debug logging.



##########
apisix/http/router/radixtree_uri_with_parameter.lua:
##########
@@ -18,24 +18,128 @@ local require = require
 local core = require("apisix.core")
 local base_router = require("apisix.http.route")
 local get_services = require("apisix.http.service").services
+local apisix_router = require("apisix.router")
+local json = require("apisix.core.json")
+local table = require("apisix.core.table")
 local cached_router_version
 local cached_service_version
+local uri_routes = {}
+local uri_router
+local match_opts = {}
 
 
 local _M = {}
 
 
-    local uri_routes = {}
-    local uri_router
-    local match_opts = {}
+local function incremental_operate_radixtree(routes)

Review Comment:
   We should put this function in radixtree_uri.lua or elsewhere and reuse it 
here, instead of redefine? They are the same except for the no_param_match.



##########
apisix/router.lua:
##########
@@ -22,30 +22,95 @@ local plugin_checker = 
require("apisix.plugin").plugin_checker
 local str_lower = string.lower
 local error   = error
 local ipairs  = ipairs
+local table        = require("apisix.core.table")
+local json         = require("apisix.core.json")
+local sub_str      = string.sub
 
 
 local _M = {version = 0.3}
 
+_M.need_create_radixtree = true
 
-local function filter(route)
+
+local function short_key(self, str)
+    return sub_str(str, #self.key + 2)
+end
+
+
+local function filter(route, pre_route_or_size, obj)
     route.orig_modifiedIndex = route.modifiedIndex
     route.update_count = 0
 
     route.has_domain = false
-    if not route.value then
-        return
+    if route.value then
+        if route.value.host then
+            route.value.host = str_lower(route.value.host)
+        elseif route.value.hosts then
+            for i, v in ipairs(route.value.hosts) do
+                route.value.hosts[i] = str_lower(v)
+            end
+        end
+
+        apisix_upstream.filter_upstream(route.value.upstream, route)
     end
 
-    if route.value.host then
-        route.value.host = str_lower(route.value.host)
-    elseif route.value.hosts then
-        for i, v in ipairs(route.value.hosts) do
-            route.value.hosts[i] = str_lower(v)
+    core.log.info("filter route: ", core.json.delay_encode(route, true))
+
+    if not obj then
+        return
+    end
+    --save sync route and operation type into a map
+    if type(pre_route_or_size) == "number" then
+        if pre_route_or_size == #obj.values then
+            _M.need_create_radixtree = true
         end
+        return
     end
 
-    apisix_upstream.filter_upstream(route.value.upstream, route)
+    local key
+    if obj.single_item then
+        key = obj.key
+    else
+        key = short_key(obj, route.key)
+    end
+
+    local sync_tb = _M.sync_tb
+    if pre_route_or_size then
+        if route.value then
+            --update route
+            core.log.notice("update routes watched from etcd into radixtree.", 
json.encode(route))
+            if not sync_tb[route.value.id] then
+                sync_tb[route.value.id] = {op = "update", last_route = 
pre_route_or_size, cur_route = route}
+            elseif sync_tb[route.value.id]["op"] == "update" then
+                sync_tb[route.value.id] = {op = "update", last_route = 
sync_tb[route.value.id]["last_route"], cur_route = route}
+            elseif sync_tb[route.value.id]["op"] == "create" then
+                sync_tb[route.value.id] = {op = "create", cur_route = route}
+            end
+        else
+            --delete route
+            core.log.notice("delete routes watched from etcd into radixtree.", 
json.encode(route))
+            if not sync_tb[key] then
+                sync_tb[key] = {op = "delete", last_route = pre_route_or_size}
+            elseif sync_tb[key]["op"] == "create" then
+                sync_tb[key] = nil
+            elseif sync_tb[key]["op"] == "update" then
+                sync_tb[key] = {op = "delete", last_route = 
sync_tb[route.value.id]["last_route"]}
+            end
+        end
+    elseif route.value then
+        --create route
+        core.log.notice("create routes watched from etcd into radixtree.", 
json.encode(route))
+        if not sync_tb[route.value.id] then
+            sync_tb[route.value.id] = {op = "create", cur_route = route}
+        elseif sync_tb[route.value.id]["op"] == "delete" then
+            sync_tb[route.value.id] = {op = "update", cur_route = route, 
last_route = sync_tb[route.value.id]["last_route"]}
+        end
+    else
+        core.log.error("invalid operation type for a route.", route.key)
+        return
+    end
 
+    _M.sync_tb = sync_tb

Review Comment:
   remove this statement, and ditto.



##########
apisix/http/router/radixtree_uri.lua:
##########
@@ -18,24 +18,128 @@ local require = require
 local core = require("apisix.core")
 local base_router = require("apisix.http.route")
 local get_services = require("apisix.http.service").services
+local apisix_router = require("apisix.router")
+local json = require("apisix.core.json")
+local table = require("apisix.core.table")
 local cached_router_version
 local cached_service_version
+local uri_routes = {}
+local uri_router
+local match_opts = {}
 
 
 local _M = {version = 0.2}
 
 
-    local uri_routes = {}
-    local uri_router
-    local match_opts = {}
+local function incremental_operate_radixtree(routes)
+    local sync_tb = apisix_router.sync_tb
+    if apisix_router.need_create_radixtree then
+        uri_router = base_router.create_radixtree_uri_router(routes, 
uri_routes, false)
+        apisix_router.need_create_radixtree = false
+        for k, _ in pairs(sync_tb) do
+            sync_tb[k] = nil
+        end
+        return
+    end
+
+    local op, route, last_route, err
+    local cur_tmp, last_tmp = {}, {}
+    local router_opts = {
+        no_param_match = true
+    }
+    for k, v in pairs(sync_tb) do
+        op = sync_tb[k]["op"]
+        route = sync_tb[k]["cur_route"]
+        last_route = sync_tb[k]["last_route"]
+        cur_tmp = {}
+        last_tmp = {}
+
+        if route and route.value then
+            local status = table.try_read_attr(route, "value", "status")
+            if status and status == 0 then
+                return

Review Comment:
   continue the loop? You could use goto statement.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to