liquid_feedback_core

diff core.sql @ 67:72e5356b5454

Improved locking to avoid unneccessary delays and possible deadlocks
author jbe
date Mon Aug 16 14:45:21 2010 +0200 (2010-08-16)
parents bdccc56fb705
children c65002c1a509
line diff
     1.1 --- a/core.sql	Sun Aug 15 17:11:02 2010 +0200
     1.2 +++ b/core.sql	Mon Aug 16 14:45:21 2010 +0200
     1.3 @@ -6,7 +6,7 @@
     1.4  BEGIN;
     1.5  
     1.6  CREATE VIEW "liquid_feedback_version" AS
     1.7 -  SELECT * FROM (VALUES ('1.2.4', 1, 2, 4))
     1.8 +  SELECT * FROM (VALUES ('1.2.5', 1, 2, 5))
     1.9    AS "subquery"("string", "major", "minor", "revision");
    1.10  
    1.11  
    1.12 @@ -1917,39 +1917,123 @@
    1.13  -- Locking for snapshots and voting procedure --
    1.14  ------------------------------------------------
    1.15  
    1.16 -CREATE FUNCTION "global_lock"() RETURNS VOID
    1.17 +
    1.18 +CREATE FUNCTION "share_row_lock_issue_trigger"()
    1.19 +  RETURNS TRIGGER
    1.20 +  LANGUAGE 'plpgsql' VOLATILE AS $$
    1.21 +    BEGIN
    1.22 +      IF TG_OP = 'UPDATE' OR TG_OP = 'DELETE' THEN
    1.23 +        PERFORM NULL FROM "issue" WHERE "id" = OLD."issue_id" FOR SHARE;
    1.24 +      END IF;
    1.25 +      IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
    1.26 +        PERFORM NULL FROM "issue" WHERE "id" = NEW."issue_id" FOR SHARE;
    1.27 +        RETURN NEW;
    1.28 +      ELSE
    1.29 +        RETURN OLD;
    1.30 +      END IF;
    1.31 +    END;
    1.32 +  $$;
    1.33 +
    1.34 +COMMENT ON FUNCTION "share_row_lock_issue_trigger"() IS 'Implementation of triggers "share_row_lock_issue" on multiple tables';
    1.35 +
    1.36 +
    1.37 +CREATE FUNCTION "share_row_lock_issue_via_initiative_trigger"()
    1.38 +  RETURNS TRIGGER
    1.39    LANGUAGE 'plpgsql' VOLATILE AS $$
    1.40      BEGIN
    1.41 -      -- NOTE: PostgreSQL allows reading, while tables are locked in
    1.42 -      -- exclusive move. Transactions should be kept short anyway!
    1.43 -      LOCK TABLE "member"     IN EXCLUSIVE MODE;
    1.44 -      LOCK TABLE "area"       IN EXCLUSIVE MODE;
    1.45 -      LOCK TABLE "membership" IN EXCLUSIVE MODE;
    1.46 -      -- NOTE: "member", "area" and "membership" are locked first to
    1.47 -      -- prevent deadlocks in combination with "calculate_member_counts"()
    1.48 -      LOCK TABLE "policy"     IN EXCLUSIVE MODE;
    1.49 -      LOCK TABLE "issue"      IN EXCLUSIVE MODE;
    1.50 -      LOCK TABLE "initiative" IN EXCLUSIVE MODE;
    1.51 -      LOCK TABLE "draft"      IN EXCLUSIVE MODE;
    1.52 -      LOCK TABLE "suggestion" IN EXCLUSIVE MODE;
    1.53 -      LOCK TABLE "interest"   IN EXCLUSIVE MODE;
    1.54 -      LOCK TABLE "initiator"  IN EXCLUSIVE MODE;
    1.55 -      LOCK TABLE "supporter"  IN EXCLUSIVE MODE;
    1.56 -      LOCK TABLE "opinion"    IN EXCLUSIVE MODE;
    1.57 -      LOCK TABLE "delegation" IN EXCLUSIVE MODE;
    1.58 +      IF TG_OP = 'UPDATE' OR TG_OP = 'DELETE' THEN
    1.59 +        PERFORM NULL FROM "issue"
    1.60 +          JOIN "initiative" ON "issue"."id" = "initiative"."issue_id"
    1.61 +          WHERE "initiative"."id" = OLD."initiative_id"
    1.62 +          FOR SHARE OF "issue";
    1.63 +      END IF;
    1.64 +      IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
    1.65 +        PERFORM NULL FROM "issue"
    1.66 +          JOIN "initiative" ON "issue"."id" = "initiative"."issue_id"
    1.67 +          WHERE "initiative"."id" = NEW."initiative_id"
    1.68 +          FOR SHARE OF "issue";
    1.69 +        RETURN NEW;
    1.70 +      ELSE
    1.71 +        RETURN OLD;
    1.72 +      END IF;
    1.73 +    END;
    1.74 +  $$;
    1.75 +
    1.76 +COMMENT ON FUNCTION "share_row_lock_issue_trigger"() IS 'Implementation of trigger "share_row_lock_issue_via_initiative" on table "opinion"';
    1.77 +
    1.78 +
    1.79 +CREATE TRIGGER "share_row_lock_issue"
    1.80 +  BEFORE INSERT OR UPDATE OR DELETE ON "initiative"
    1.81 +  FOR EACH ROW EXECUTE PROCEDURE
    1.82 +  "share_row_lock_issue_trigger"();
    1.83 +
    1.84 +CREATE TRIGGER "share_row_lock_issue"
    1.85 +  BEFORE INSERT OR UPDATE OR DELETE ON "interest"
    1.86 +  FOR EACH ROW EXECUTE PROCEDURE
    1.87 +  "share_row_lock_issue_trigger"();
    1.88 +
    1.89 +CREATE TRIGGER "share_row_lock_issue"
    1.90 +  BEFORE INSERT OR UPDATE OR DELETE ON "supporter"
    1.91 +  FOR EACH ROW EXECUTE PROCEDURE
    1.92 +  "share_row_lock_issue_trigger"();
    1.93 +
    1.94 +CREATE TRIGGER "share_row_lock_issue_via_initiative"
    1.95 +  BEFORE INSERT OR UPDATE OR DELETE ON "opinion"
    1.96 +  FOR EACH ROW EXECUTE PROCEDURE
    1.97 +  "share_row_lock_issue_via_initiative_trigger"();
    1.98 +
    1.99 +CREATE TRIGGER "share_row_lock_issue"
   1.100 +  BEFORE INSERT OR UPDATE OR DELETE ON "direct_voter"
   1.101 +  FOR EACH ROW EXECUTE PROCEDURE
   1.102 +  "share_row_lock_issue_trigger"();
   1.103 +
   1.104 +CREATE TRIGGER "share_row_lock_issue"
   1.105 +  BEFORE INSERT OR UPDATE OR DELETE ON "delegating_voter"
   1.106 +  FOR EACH ROW EXECUTE PROCEDURE
   1.107 +  "share_row_lock_issue_trigger"();
   1.108 +
   1.109 +CREATE TRIGGER "share_row_lock_issue"
   1.110 +  BEFORE INSERT OR UPDATE OR DELETE ON "vote"
   1.111 +  FOR EACH ROW EXECUTE PROCEDURE
   1.112 +  "share_row_lock_issue_trigger"();
   1.113 +
   1.114 +COMMENT ON TRIGGER "share_row_lock_issue"                ON "initiative"       IS 'See "lock_issue" function';
   1.115 +COMMENT ON TRIGGER "share_row_lock_issue"                ON "interest"         IS 'See "lock_issue" function';
   1.116 +COMMENT ON TRIGGER "share_row_lock_issue"                ON "supporter"        IS 'See "lock_issue" function';
   1.117 +COMMENT ON TRIGGER "share_row_lock_issue_via_initiative" ON "opinion"          IS 'See "lock_issue" function';
   1.118 +COMMENT ON TRIGGER "share_row_lock_issue"                ON "direct_voter"     IS 'See "lock_issue" function';
   1.119 +COMMENT ON TRIGGER "share_row_lock_issue"                ON "delegating_voter" IS 'See "lock_issue" function';
   1.120 +COMMENT ON TRIGGER "share_row_lock_issue"                ON "vote"             IS 'See "lock_issue" function';
   1.121 +
   1.122 +
   1.123 +CREATE FUNCTION "lock_issue"
   1.124 +  ( "issue_id_p" "issue"."id"%TYPE )
   1.125 +  RETURNS VOID
   1.126 +  LANGUAGE 'plpgsql' VOLATILE AS $$
   1.127 +    BEGIN
   1.128 +      LOCK TABLE "member"     IN SHARE MODE;
   1.129 +      LOCK TABLE "membership" IN SHARE MODE;
   1.130 +      LOCK TABLE "policy"     IN SHARE MODE;
   1.131 +      PERFORM NULL FROM "issue" WHERE "id" = "issue_id_p" FOR UPDATE;
   1.132 +      -- NOTE: The row-level exclusive lock in combination with the
   1.133 +      -- share_row_lock_issue(_via_initiative)_trigger functions (which
   1.134 +      -- acquire a row-level share lock on the issue) ensure that no data
   1.135 +      -- is changed, which could affect calculation of snapshots or
   1.136 +      -- counting of votes. Table "delegation" must be table-level-locked,
   1.137 +      -- as it also contains issue- and global-scope delegations.
   1.138 +      LOCK TABLE "delegation" IN SHARE MODE;
   1.139        LOCK TABLE "direct_population_snapshot"     IN EXCLUSIVE MODE;
   1.140        LOCK TABLE "delegating_population_snapshot" IN EXCLUSIVE MODE;
   1.141        LOCK TABLE "direct_interest_snapshot"       IN EXCLUSIVE MODE;
   1.142        LOCK TABLE "delegating_interest_snapshot"   IN EXCLUSIVE MODE;
   1.143        LOCK TABLE "direct_supporter_snapshot"      IN EXCLUSIVE MODE;
   1.144 -      LOCK TABLE "direct_voter"     IN EXCLUSIVE MODE;
   1.145 -      LOCK TABLE "delegating_voter" IN EXCLUSIVE MODE;
   1.146 -      LOCK TABLE "vote"             IN EXCLUSIVE MODE;
   1.147        RETURN;
   1.148      END;
   1.149    $$;
   1.150  
   1.151 -COMMENT ON FUNCTION "global_lock"() IS 'Locks all tables related to support/voting until end of transaction; read access is still possible though';
   1.152 +COMMENT ON FUNCTION "lock_issue"
   1.153 +  ( "issue"."id"%TYPE )
   1.154 +  IS 'Locks the issue and all other data which is used for calculating snapshots or counting votes.';
   1.155  
   1.156  
   1.157  
   1.158 @@ -1961,9 +2045,10 @@
   1.159    RETURNS VOID
   1.160    LANGUAGE 'plpgsql' VOLATILE AS $$
   1.161      BEGIN
   1.162 -      LOCK TABLE "member"     IN EXCLUSIVE MODE;
   1.163 -      LOCK TABLE "area"       IN EXCLUSIVE MODE;
   1.164 -      LOCK TABLE "membership" IN EXCLUSIVE MODE;
   1.165 +      LOCK TABLE "member"       IN SHARE MODE;
   1.166 +      LOCK TABLE "member_count" IN EXCLUSIVE MODE;
   1.167 +      LOCK TABLE "area"         IN EXCLUSIVE MODE;
   1.168 +      LOCK TABLE "membership"   IN SHARE MODE;
   1.169        DELETE FROM "member_count";
   1.170        INSERT INTO "member_count" ("total_count")
   1.171          SELECT "total_count" FROM "member_count_view";
   1.172 @@ -2109,7 +2194,7 @@
   1.173    $$;
   1.174  
   1.175  COMMENT ON FUNCTION "create_population_snapshot"
   1.176 -  ( "issue_id_p" "issue"."id"%TYPE )
   1.177 +  ( "issue"."id"%TYPE )
   1.178    IS 'This function creates a new ''periodic'' population snapshot for the given issue. It does neither lock any tables, nor updates precalculated values in other tables.';
   1.179  
   1.180  
   1.181 @@ -2269,7 +2354,7 @@
   1.182        "initiative_id_v"    "initiative"."id"%TYPE;
   1.183        "suggestion_id_v"    "suggestion"."id"%TYPE;
   1.184      BEGIN
   1.185 -      PERFORM "global_lock"();
   1.186 +      PERFORM "lock_issue"("issue_id_p");
   1.187        PERFORM "create_population_snapshot"("issue_id_p");
   1.188        PERFORM "create_interest_snapshot"("issue_id_p");
   1.189        UPDATE "issue" SET
   1.190 @@ -2668,7 +2753,7 @@
   1.191        "issue_row"   "issue"%ROWTYPE;
   1.192        "member_id_v" "member"."id"%TYPE;
   1.193      BEGIN
   1.194 -      PERFORM "global_lock"();
   1.195 +      PERFORM "lock_issue"("issue_id_p");
   1.196        SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p";
   1.197        DELETE FROM "delegating_voter"
   1.198          WHERE "issue_id" = "issue_id_p";
   1.199 @@ -3068,7 +3153,7 @@
   1.200        "policy_row"        "policy"%ROWTYPE;
   1.201        "voting_requested_v" BOOLEAN;
   1.202      BEGIN
   1.203 -      PERFORM "global_lock"();
   1.204 +      PERFORM "lock_issue"("issue_id_p");
   1.205        SELECT * INTO "issue_row" FROM "issue" WHERE "id" = "issue_id_p";
   1.206        -- only process open issues:
   1.207        IF "issue_row"."closed" ISNULL THEN

Impressum / About Us