Feature: new PGPOOL SET CACHE DELETE command.
authorTatsuo Ishii <ishii@postgresql.org>
Mon, 21 Oct 2024 10:17:56 +0000 (19:17 +0900)
committerTatsuo Ishii <ishii@postgresql.org>
Mon, 21 Oct 2024 10:17:56 +0000 (19:17 +0900)
The new PGPOOl SET command allows to delete query cache by specifying
the previous query used to create the query cache entry. example usage
is:

PGPOOL SET CACHE DELETE 'SELECT * FROM t1;'

This command is particularly useful for queries that are not
invalidated by the auto cache invalidation feature because the query
does not have any reference to tables.

doc.ja/src/sgml/ref/allfiles.sgml
doc.ja/src/sgml/ref/pgpool_set_cache.sgml [new file with mode: 0644]
doc.ja/src/sgml/reference.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/pgpool_set_cache.sgml [new file with mode: 0644]
doc/src/sgml/reference.sgml
src/include/parser/nodes.h
src/include/query_cache/pool_memqcache.h
src/parser/gram.y
src/protocol/pool_proto_modules.c
src/query_cache/pool_memqcache.c

index 6764e8f9cd30e2c86632cf9055c3fda30e5b6097..89be154bc1d2e5ab293f0d8d8dc680a8332fce58 100644 (file)
@@ -30,6 +30,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY pgpoolShow          SYSTEM "pgpool_show.sgml">
 <!ENTITY pgpoolReset         SYSTEM "pgpool_reset.sgml">
 <!ENTITY pgpoolSet           SYSTEM "pgpool_set.sgml">
+<!ENTITY pgpoolSetCache      SYSTEM "pgpool_set_cache.sgml">
 <!ENTITY showPoolStatus      SYSTEM "show_pool_status.sgml">
 <!ENTITY showPoolNodes       SYSTEM "show_pool_nodes.sgml">
 <!ENTITY showPoolProcesses   SYSTEM "show_pool_processes.sgml">
diff --git a/doc.ja/src/sgml/ref/pgpool_set_cache.sgml b/doc.ja/src/sgml/ref/pgpool_set_cache.sgml
new file mode 100644 (file)
index 0000000..82f0235
--- /dev/null
@@ -0,0 +1,131 @@
+<!--
+doc/src/sgml/ref/set.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-PGPOOL-SET-CACHE">
+ <indexterm zone="sql-pgpool-set-cache">
+  <primary>PGPOOL SET</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>PGPOOL SET CACHE</refentrytitle>
+  <manvolnum>1</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>PGPOOL SET CACHE</refname>
+<!--
+  <refpurpose>delete query cache</refpurpose>
+-->
+  <refpurpose>クエリキャッシュを削除する</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+  <synopsis>
+   PGPOOL SET CACHE DELETE <replaceable class="PARAMETER">'query'</replaceable>
+  </synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+<!--
+  <title>Description</title>
+-->
+  <title>説明</title>
+
+  <para>
+<!--
+   The <command>PGPOOL SET CACHE DELETE</command> command deletes the
+   <link linkend="runtime-in-memory-query-cache">query cache</link>
+   previously created by the query. The query string must be exactly
+   identical to the previous query, including
+   trailing <literal>';'</literal>. Nevertheless if the query
+   includes <literal>'</literal> (single quore), it needs to be
+   prefixed by <literal>'</literal> like:
+-->
+   <command>PGPOOL SET CACHE DELETE</command>コマンドは、以前のクエリで作成された<link linkend="runtime-in-memory-query-cache">クエリキャッシュ</link>を削除します。
+   クエリ文字列は、末尾の<literal>';'</literal>も含めて以前のクエリと完全に一致していなければなりません。
+   にもかかわらず、<literal>'</literal> (単一引用符)がクエリに含まれている場合は、以下のように<literal>'</literal>を前に追加する必要があります。
+   <programlisting>
+PGPOOL SET CACHE DELETE 'SELECT ''FOO'';';
+   </programlisting>
+<!--
+   This command is particularly useful for queries that are not
+   invalidated by
+   the <link linkend="guc-memqcache-auto-cache-invalidation">auto
+   cache invalidation feature</link> because the query does not have
+   any reference to tables.
+-->
+   このコマンドはとりわけ、クエリ中にテーブル参照を含んでいないために<link linkend="guc-memqcache-auto-cache-invalidation">自動キャッシュ削除機能</link>で削除されないクエリに有用です。
+  </para>
+  <para>
+<!--
+   If you do not remember the previous query, you can
+   use <xref linkend="PCP-INVALIDATE-QUERY-CACHE"> to delete all the
+   query cache including the cache you want to delete. However it
+   requires the <productname>Pgpool-II</productname> admin privilege.
+-->
+    以前のクエリが思い出せない時は、<xref linkend="PCP-INVALIDATE-QUERY-CACHE">を使って、削除したいクエリキャッシュも含み、すべてのクエリキャッシュを削除することができます。
+    ただし、これは<productname>Pgpool-II</productname>の管理者権限が必要になります。
+  </para>
+  <para>
+<!--
+   Upon successful deletion of the query cache, this command emits a
+   notice message:
+-->
+   クエリキャッシュの削除に成功すると、以下のNOTICEメッセージを表示します。
+   <programlisting>
+NOTICE:  query cache deleted. query: "<literal>query string</literal>"
+   </programlisting>
+<!--
+   If the cache does not exist or query cache is not enabled, it emits
+   a notice message:
+-->
+   クエリキャッシュが存在しないか、クエリキャッシュが無効の場合には、以下のNOTICEメッセージを表示します。
+   <programlisting>
+NOTICE:  query cache does not exist for query: "<literal>query string</literal>"
+   </programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+<!--
+  <title>Examples</title>
+-->
+  <title>例</title>
+
+  <para>
+<!--
+   Accidentally created an unwanted query cache. Delete it by using
+   the command.
+-->
+   間違って希望しないクエリキャッシュを作成してしまいました。
+   以下のコマンドで削除します。
+    <programlisting>
+test=# /*FORCE QUERY CACHE*/SELECT current_timestamp;
+       current_timestamp       
+-------------------------------
+ 2024-10-18 18:25:07.826423+09
+(1 row)
+
+test=# PGPOOL SET CACHE DELETE '/*FORCE QUERY CACHE*/SELECT current_timestamp;';
+NOTICE:  query cache deleted. query: "/*FORCE QUERY CACHE*/SELECT current_timestamp;"
+SET
+    </programlisting>
+  </para>
+
+ </refsect1>
+
+ <refsect1>
+<!--
+  <title>See Also</title>
+-->
+  <title>関連項目</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="PCP-INVALIDATE-QUERY-CACHE"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
index 548b05f24b17f7d8adef443e7cdad485f9f11a8a..adfe8e1e18f8c8474e158c951188fd5ac8244b2a 100644 (file)
 
   &pgpoolShow
   &pgpoolSet
+  &pgpoolSetCache
   &pgpoolReset
   &showPoolStatus
   &showPoolNodes
index 6764e8f9cd30e2c86632cf9055c3fda30e5b6097..89be154bc1d2e5ab293f0d8d8dc680a8332fce58 100644 (file)
@@ -30,6 +30,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY pgpoolShow          SYSTEM "pgpool_show.sgml">
 <!ENTITY pgpoolReset         SYSTEM "pgpool_reset.sgml">
 <!ENTITY pgpoolSet           SYSTEM "pgpool_set.sgml">
+<!ENTITY pgpoolSetCache      SYSTEM "pgpool_set_cache.sgml">
 <!ENTITY showPoolStatus      SYSTEM "show_pool_status.sgml">
 <!ENTITY showPoolNodes       SYSTEM "show_pool_nodes.sgml">
 <!ENTITY showPoolProcesses   SYSTEM "show_pool_processes.sgml">
diff --git a/doc/src/sgml/ref/pgpool_set_cache.sgml b/doc/src/sgml/ref/pgpool_set_cache.sgml
new file mode 100644 (file)
index 0000000..8ae8d42
--- /dev/null
@@ -0,0 +1,97 @@
+<!--
+doc/src/sgml/ref/set.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-PGPOOL-SET-CACHE">
+ <indexterm zone="sql-pgpool-set-cache">
+  <primary>PGPOOL SET</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>PGPOOL SET CACHE</refentrytitle>
+  <manvolnum>1</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>PGPOOL SET CACHE</refname>
+  <refpurpose>delete query cache</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+  <synopsis>
+   PGPOOL SET CACHE DELETE <replaceable class="PARAMETER">'query'</replaceable>
+  </synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   The <command>PGPOOL SET CACHE DELETE</command> command deletes the
+   <link linkend="runtime-in-memory-query-cache">query cache</link>
+   previously created by the query. The query string must be exactly
+   identical to the previous query, including
+   trailing <literal>';'</literal>. Nevertheless if the query
+   includes <literal>'</literal> (single quore), it needs to be
+   prefixed by <literal>'</literal> like:
+   <programlisting>
+PGPOOL SET CACHE DELETE 'SELECT ''FOO'';';
+   </programlisting>
+   This command is particularly useful for queries that are not
+   invalidated by
+   the <link linkend="guc-memqcache-auto-cache-invalidation">auto
+   cache invalidation feature</link> because the query does not have
+   any reference to tables.
+  </para>
+  <para>
+   If you do not remember the previous query, you can
+   use <xref linkend="PCP-INVALIDATE-QUERY-CACHE"> to delete all the
+   query cache including the cache you want to delete. However it
+   requires the <productname>Pgpool-II</productname> admin privilege.
+  </para>
+  <para>
+   Upon successful deletion of the query cache, this command emits a
+   notice message:
+   <programlisting>
+NOTICE:  query cache deleted. query: "<literal>query string</literal>"
+   </programlisting>
+   If the cache does not exist or query cache is not enabled, it emits
+   a notice message:
+   <programlisting>
+NOTICE:  query cache does not exist for query: "<literal>query string</literal>"
+   </programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Accidentally created an unwanted query cache. Delete it by using
+   the command.
+    <programlisting>
+test=# /*FORCE QUERY CACHE*/SELECT current_timestamp;
+       current_timestamp       
+-------------------------------
+ 2024-10-18 18:25:07.826423+09
+(1 row)
+
+test=# PGPOOL SET CACHE DELETE '/*FORCE QUERY CACHE*/SELECT current_timestamp;';
+NOTICE:  query cache deleted. query: "/*FORCE QUERY CACHE*/SELECT current_timestamp;"
+SET
+    </programlisting>
+  </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="PCP-INVALIDATE-QUERY-CACHE"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
index ffc7ddb8674be8f48600d8ea5017558cea627a28..2b2db4947cc8404b038cc71b036f50da210d6da4 100644 (file)
 
   &pgpoolShow
   &pgpoolSet
+  &pgpoolSetCache
   &pgpoolReset
   &showPoolStatus
   &showPoolNodes
index 734faa48f348a90c2f8f9b4a3bcd932e8ea6bb84..1d5bb3ed627a6b20a7f8c54641cbb99fa88b48ef 100644 (file)
@@ -34,6 +34,7 @@ typedef enum NodeTag
        /* pgpool Extension */
                                T_PgpoolVariableSetStmt,
                                T_PgpoolVariableShowStmt,
+                               T_PgpoolQueryCacheStmt,
 #include "nodetags.h"
 } NodeTag;
 
index 5a0a1fce92573b701c494b7a55c281196e8e5392..042121fc4d0fef11912f58bbe5319c3fe5cd6fbd 100644 (file)
@@ -307,4 +307,6 @@ extern void pool_init_whole_cache_blocks(void);
 
 extern void clear_query_cache(void);
 
+extern bool query_cache_delete_by_stmt(char *query, POOL_CONNECTION_POOL * backend);
+
 #endif                                                 /* POOL_MEMQCACHE_H */
index a39f98991740638d25b68080f3205088415914df..61663cce2ea8ea4de1a56dae70e68594ab75738e 100644 (file)
@@ -1655,6 +1655,13 @@ VariableSetStmt:
                                        n->is_local = false;
                                        $$ = (Node *) n;
                                }
+                       | PGPOOL set_rest_more
+                               {
+                                       VariableSetStmt *n = $2;
+                                       n->type = T_PgpoolQueryCacheStmt; /* Hack to keep changes minimum */
+                                       n->is_local = false;
+                                       $$ = (Node *) n;
+                               }
                        | SET set_rest
                                {
                                        VariableSetStmt *n = $2;
@@ -1834,6 +1841,13 @@ set_rest_more:   /* Generic SET syntaxes: */
                                        n->args = list_make1(makeStringConst($3, @3));
                                        $$ = n;
                                }
+                       /* PGPOOL CACHE DELETE */
+                       | SET CACHE DELETE_P Sconst
+                               {
+                                       VariableSetStmt *n = makeNode(VariableSetStmt);
+                                       n->name = $4;   /* query to delete query cache */
+                                       $$ = n;
+                               }
                ;
 
 var_name:      ColId                                                           { $$ = $1; }
index df8d27e0ca957bf2140ca534fbdc486695d97757..4988325dcad1de1379dc45f174e6df51d6a37392 100644 (file)
@@ -450,7 +450,20 @@ SimpleQuery(POOL_CONNECTION * frontend,
                        pool_set_skip_reading_from_backends();
                        return POOL_CONTINUE;
                }
+               if (IsA(node, PgpoolQueryCacheStmt))
+               {
+                       VariableSetStmt *vnode = (VariableSetStmt *) node;
 
+                       if (query_cache_delete_by_stmt(vnode->name, backend))
+                               elog(NOTICE, "query cache deleted. query: \"%s\"", vnode->name);
+                       else
+                               elog(NOTICE, "query cache does not exist for query: \"%s\"", vnode->name);
+                       pool_ps_idle_display(backend);
+                       send_complete_and_ready(frontend, backend, "SET", -1);
+                       pool_query_context_destroy(query_context);
+                       pool_set_skip_reading_from_backends();
+                       return POOL_CONTINUE;
+               }
                if (IsA(node, PgpoolVariableSetStmt))
                {
                        VariableSetStmt *vnode = (VariableSetStmt *) node;
index 4c55f0fc404a0a26a1742671e6ebefe12e77c407..7f824fe2277fd2487964479c08e6e48460e5c811 100644 (file)
@@ -4731,3 +4731,56 @@ InvalidateQueryCache(int tableoid, int dboid)
        pool_semaphore_unlock(QUERY_CACHE_STATS_SEM);
        POOL_SETMASK(&oldmask);
 }
+
+/*
+ * Public API to invalidate query cache specified by query string.  Returns
+ * true for successfully query cache invalidation.  If the query cache was not
+ * found, returns false.  Note that this function does not remove any entry in
+ * a table oid file.  That may leave a garbage in the file (which is ignored
+ * by the auto cache invalidation) but it's not worth the trouble to remove
+ * the entry since it's relatively expensive. It needs to rewrite the whole
+ * file in the worst case.
+ */
+bool query_cache_delete_by_stmt(char *query, POOL_CONNECTION_POOL * backend)
+{
+       bool            rtn = true;
+       pool_sigset_t oldmask;
+
+       char            key[MAX_KEY];
+       POOL_CACHEID *cacheid;
+
+       POOL_SETMASK2(&BlockSig, &oldmask);
+       pool_shmem_lock(POOL_MEMQ_EXCLUSIVE_LOCK);
+
+       /* encode md5key */
+       encode_key(query, key, backend);
+
+       if (pool_is_shmem_cache())
+
+       {
+               POOL_QUERY_HASH         hashkey;
+
+               memcpy(hashkey.query_hash, key, POOL_MD5_HASHKEYLEN);
+               cacheid = pool_hash_search(&hashkey);
+               if (cacheid == NULL)
+                       rtn = false;
+               else
+                       pool_delete_item_shmem_cache(cacheid);
+       }
+#ifdef USE_MEMCACHED
+       else
+       {
+               if (delete_cache_on_memcached(key) == 0)
+                       rtn = false;
+       }
+#else
+       {
+               ereport(WARNING,
+                               (errmsg("failed to delete query cache on memcached, memcached support is not enabled")));
+       }
+#endif
+       pool_semaphore_unlock(QUERY_CACHE_STATS_SEM);
+       POOL_SETMASK(&oldmask);
+
+       return rtn;
+}