From: Terry Burton Date: Tue, 8 Sep 2020 18:06:30 +0000 (+0100) Subject: sqlippools: allocate_requested queries for MySQL X-Git-Tag: release_3_0_22~433 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ee99155fbf583519fa98654daf2cf21db20df33d;p=thirdparty%2Ffreeradius-server.git sqlippools: allocate_requested queries for MySQL --- diff --git a/raddb/mods-config/sql/ippool-dhcp/mysql/procedure-no-skip-locked.sql b/raddb/mods-config/sql/ippool-dhcp/mysql/procedure-no-skip-locked.sql index 229df13b5e5..8d919c711ac 100644 --- a/raddb/mods-config/sql/ippool-dhcp/mysql/procedure-no-skip-locked.sql +++ b/raddb/mods-config/sql/ippool-dhcp/mysql/procedure-no-skip-locked.sql @@ -33,7 +33,8 @@ -- '%{control:${pool_name}}', \ -- '%{DHCP-Gateway-IP-Address}', \ -- '${pool_key}', \ --- ${lease_duration} \ +-- ${lease_duration}, \ +-- '%{%{${req_attribute_name}}:-0.0.0.0}' \ -- )" -- allocate_update = "" -- allocate_commit = "" @@ -46,13 +47,23 @@ CREATE PROCEDURE fr_allocate_previous_or_new_framedipaddress ( IN v_pool_name VARCHAR(64), IN v_gateway VARCHAR(15), IN v_pool_key VARCHAR(64), - IN v_lease_duration INT + IN v_lease_duration INT, + IN v_requested_address VARCHAR(15) ) proc:BEGIN DECLARE r_address VARCHAR(15); -- Reissue an existing IP address lease when re-authenticating a session -- + -- Note: In this query we get away without the need for FOR UPDATE + -- becase: + -- + -- (a) Each existing lease only belongs to a single device, so + -- no two devices will be racing over a single address. + -- (b) The set of existing leases (not yet expired) are + -- disjoint from the set of free leases, so not subject to + -- reallocation. + -- SELECT framedipaddress INTO r_address FROM dhcpippool WHERE pool_name = v_pool_name @@ -75,6 +86,15 @@ proc:BEGIN -- AND `status` IN ('dynamic', 'static') -- LIMIT 1; + -- + -- Normally here we would honour an IP address hint if the IP were + -- available, however we cannot do that without taking a lock which + -- defeats the purpose of this version of the stored procedure. + -- + -- It you need to honour an IP address hint then use a database with + -- support for SKIP LOCKED and use the normal stored procedure. + -- + IF r_address IS NOT NULL THEN UPDATE dhcpippool SET diff --git a/raddb/mods-config/sql/ippool-dhcp/mysql/procedure.sql b/raddb/mods-config/sql/ippool-dhcp/mysql/procedure.sql index e9eb7d4911b..d3f9dd5d2c4 100644 --- a/raddb/mods-config/sql/ippool-dhcp/mysql/procedure.sql +++ b/raddb/mods-config/sql/ippool-dhcp/mysql/procedure.sql @@ -21,7 +21,8 @@ -- '%{control:${pool_name}}', \ -- '%{DHCP-Gateway-IP-Address}', \ -- '${pool_key}', \ --- ${lease_duration} \ +-- ${lease_duration}, \ +-- '%{%{${req_attribute_name}}:-0.0.0.0}' \ -- )" -- allocate_update = "" -- allocate_commit = "" @@ -34,7 +35,8 @@ CREATE PROCEDURE fr_dhcp_allocate_previous_or_new_framedipaddress ( IN v_pool_name VARCHAR(30), IN v_gateway VARCHAR(15), IN v_pool_key VARCHAR(30), - IN v_lease_duration INT + IN v_lease_duration INT, + IN v_requested_address VARCHAR(15) ) proc:BEGIN DECLARE r_address VARCHAR(15); @@ -79,6 +81,19 @@ proc:BEGIN -- FOR UPDATE; -- -- FOR UPDATE SKIP LOCKED; -- Better performance, but limited support + -- Issue the requested IP address if it is available + -- + IF r_address IS NULL AND v_requested_address <> '0.0.0.0' THEN + SELECT framedipaddress INTO r_address + FROM dhcpippool + WHERE pool_name = v_pool_name + AND framedipaddress = v_requested_address + AND `status` = 'dynamic' + AND ( pool_key = v_pool_key OR expiry_time < NOW() ) + FOR UPDATE; +-- FOR UPDATE SKIP LOCKED; -- Better performance, but limited support + END IF; + -- If we didn't reallocate a previous address then pick the least -- recently used address from the pool which maximises the likelihood -- of re-assigning the other addresses to their recent user diff --git a/raddb/mods-config/sql/ippool-dhcp/mysql/queries.conf b/raddb/mods-config/sql/ippool-dhcp/mysql/queries.conf index 95db82cd040..c9b91472984 100644 --- a/raddb/mods-config/sql/ippool-dhcp/mysql/queries.conf +++ b/raddb/mods-config/sql/ippool-dhcp/mysql/queries.conf @@ -22,6 +22,17 @@ allocate_existing = "\ AND `status` IN ('dynamic', 'static') \ ORDER BY expiry_time DESC LIMIT 1 FOR UPDATE SKIP LOCKED" +# +# Determine whether the requested IP address is available +# +allocate_requested = "\ + SELECT framedipaddress FROM ${ippool_table} \ + WHERE pool_name = '%{control:${pool_name}}' \ + AND framedipaddress = '%{%{${req_attribute_name}}:-0.0.0.0}' \ + AND `status` = 'dynamic' \ + AND ( pool_key = '${pool_key}' OR expiry_time < NOW() ) \ + FOR UPDATE SKIP LOCKED" + # # If the existing address can't be found this query will be run to # find a free address @@ -40,22 +51,29 @@ allocate_find = "\ # # -# Alternatively do both operations in one query. Depending on transaction +# Alternatively do the operations in one query. Depending on transaction # isolation mode, this can cause deadlocks # #allocate_find = "\ -# (SELECT framedipaddress, pool_key, expiry_time FROM ${ippool_table} \ +# (SELECT framedipaddress, 1 AS o FROM ${ippool_table} \ # WHERE pool_name = '%{control:${pool_name}}' \ # AND pool_key = '${pool_key}' \ # AND `status` IN ('dynamic', 'static') \ # ORDER BY expiry_time DESC LIMIT 1 FOR UPDATE SKIP LOCKED \ # ) UNION ( \ -# SELECT framedipaddress, pool_key, expiry_time FROM ${ippool_table} \ +# SELECT framedipaddress, 2 AS o FROM ${ippool_table} \ +# WHERE pool_name = '%{control:${pool_name}}' \ +# AND framedipaddress = '%{%{${req_attribute_name}}:-0.0.0.0}' \ +# AND `status` = 'dynamic' \ +# AND ( pool_key = '${pool_key}' OR expiry_time < NOW() ) \ +# FOR UPDATE SKIP LOCKED \ +# ) UNION ( \ +# SELECT framedipaddress, 3 AS o FROM ${ippool_table} \ # WHERE pool_name = '%{control:${pool_name}}' \ # AND expiry_time < NOW() \ # AND `status` = 'dynamic' \ # ORDER BY expiry_time LIMIT 1 FOR UPDATE SKIP LOCKED \ -# ) ORDER BY (pool_key <> '${pool_key}'), expiry_time \ +# ) ORDER BY o \ # LIMIT 1" # @@ -118,7 +136,8 @@ allocate_update = "\ # '%{control:${pool_name}}', \ # '%{DHCP-Gateway-IP-Address}', \ # '${pool_key}', \ -# ${offer_duration} \ +# ${offer_duration}, \ +# '%{%{${req_attribute_name}}:-0.0.0.0}' \ # )" #allocate_update = "" #allocate_commit = ""