Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add garbage collection cleanup admin and cron job #227

Merged
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c4d2efa
Add garbage collection cleanup admin and cron job
markkelnar Jul 11, 2023
ebe21a2
Delete in smaller batch events instead of all posts
markkelnar Jul 12, 2023
dc3db02
bump some version numbers
markkelnar Jul 14, 2023
63d3716
Add tests for admin settings garbage collection options
markkelnar Jul 14, 2023
81b4885
Add garbage collection unit tests
markkelnar Jul 14, 2023
e9166b8
Merge remote-tracking branch 'origin/main' into feature/garbage-colle…
markkelnar Jul 14, 2023
456d979
Get age from settings value in utils class
markkelnar Jul 14, 2023
9ff1e21
Add admin editor ability to skip garbage collection per query
markkelnar Jul 14, 2023
378ebed
Add tax_query to gargage collection to ignore where docs opt out
markkelnar Jul 18, 2023
1334dfc
Rename garbage collection class
markkelnar Jul 18, 2023
025b7c7
Add groups collection for documents. Refactor garbage collection to i…
markkelnar Jul 20, 2023
f8e6a7a
fix code sniff white space
markkelnar Jul 20, 2023
72dd923
Update gc text in graphql settings to talk about groups
markkelnar Jul 20, 2023
0f7baaa
Add the word "delete" to text
markkelnar Jul 20, 2023
c99ab11
Merge remote-tracking branch 'origin/main' into feature/garbage-colle…
markkelnar Jul 21, 2023
614a874
Use garbage_collect instead of gc
markkelnar Jul 24, 2023
a55d6e9
text description change.
markkelnar Jul 24, 2023
037df6e
snake case function name
markkelnar Jul 24, 2023
5af7178
Merge remote-tracking branch 'me/feature/garbage-collect-aged-queries…
markkelnar Jul 24, 2023
55fc784
fix for consistent names
markkelnar Jul 24, 2023
514894a
enable admin quick-edit for document groups
markkelnar Jul 24, 2023
b65efd9
Merge remote-tracking branch 'origin/main' into feature/garbage-colle…
markkelnar Jul 25, 2023
22cf8f5
Merge commit '9a023bfd9243b02f80861c56fab9c20d04c4a79a' into feature/…
jasonbahl Jul 25, 2023
35168d9
re-add allow-plugin for codesniffer-installer
markkelnar Jul 26, 2023
294bbee
Merge remote-tracking branch 'me/feature/garbage-collect-aged-queries…
markkelnar Jul 26, 2023
19923e0
add filter for garbage collect recurrence
markkelnar Jul 26, 2023
2495e72
merge fixes
markkelnar Jul 28, 2023
3a7f141
composer update
markkelnar Jul 28, 2023
92dbc42
Merge branch 'main' into feature/garbage-collect-aged-queries
jasonbahl Aug 9, 2023
7923d53
fix phpstan suggestions
markkelnar Aug 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ MYSQL_USER=${DB_USER}
MYSQL_PASSWORD=${DB_PASSWORD}

# docker container env vars
WP_VERSION=5.9
PHP_VERSION=8.0
WP_VERSION=6.1
PHP_VERSION=8.1
jasonbahl marked this conversation as resolved.
Show resolved Hide resolved
WPGRAPHQL_VERSION=latest
DATA_DUMP_DIR=/var/www/html/wp-content/plugins/wp-graphql-smart-cache/tests/_data
4 changes: 2 additions & 2 deletions .env.testing
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ MYSQL_USER=${DB_USER}
MYSQL_PASSWORD=${DB_PASSWORD}

# docker container env vars
WP_VERSION=5.9
PHP_VERSION=8.0
WP_VERSION=6.1
PHP_VERSION=8.1
WPGRAPHQL_VERSION=latest
DATA_DUMP_DIR=/var/www/html/wp-content/plugins/wp-graphql-smart-cache/tests/_data
365 changes: 144 additions & 221 deletions composer.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ services:
local:

app_db:
image: mariadb:10.2
image: mariadb:10.11
env_file:
- .env.dist
ports:
Expand All @@ -46,7 +46,7 @@ services:
local:

testing_db:
image: mariadb:10.2
image: mariadb:10.11
env_file:
- .env.testing
ports:
Expand Down
50 changes: 50 additions & 0 deletions src/Admin/Editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use WPGraphQL\SmartCache\Document;
use WPGraphQL\SmartCache\Document\Grant;
use WPGraphQL\SmartCache\Document\MaxAge;
use WPGraphQL\SmartCache\Document\GarbageCollection;
markkelnar marked this conversation as resolved.
Show resolved Hide resolved
use GraphQL\Error\SyntaxError;
use GraphQL\Server\RequestError;

Expand Down Expand Up @@ -93,6 +94,16 @@ public function is_valid_form( $post_id ) {
return;
}

// ---- Garbage Collection Options ----
if ( ! isset( $_REQUEST['savedquery_skip_gc_noncename'] ) ) {
return;
}

// phpcs:ignore
if ( ! wp_verify_nonce( $_REQUEST['savedquery_skip_gc_noncename'], 'graphql_query_skip_gc' ) ) {
return;
}

return true;
}

Expand All @@ -118,6 +129,14 @@ public function save_document_cb( $post_id, $post ) {
// phpcs:ignore
$data = sanitize_text_field( wp_unslash( $_POST['graphql_query_maxage'] ) );
$max_age->save( $post_id, $data );

$garbage = new GarbageCollection();
// phpcs:ignore
if ( isset( $_POST['graphql_query_skip_gc'] ) && GarbageCollection::DISABLED === sanitize_text_field( wp_unslash( $_POST['graphql_query_skip_gc'] ) ) ) {
$garbage->disable( $post_id );
} else {
$garbage->enable( $post_id );
}
} catch ( SyntaxError $e ) {
AdminErrors::add_message( 'Did not save invalid graphql query string. ' . $post['post_content'] );
} catch ( RequestError $e ) {
Expand Down Expand Up @@ -238,4 +257,35 @@ public function wp_editor_settings( $settings, $editor_id ) {

return $settings;
}

/**
* Draw the input field to skip garbage collection
*/
public static function skip_garbage_collection_input_box_cb( $post ) {
wp_nonce_field( 'graphql_query_skip_gc', 'savedquery_skip_gc_noncename' );

$object = new GarbageCollection();

$html = sprintf(
'<input type="checkbox" id="graphql_query_skip_gc" name="graphql_query_skip_gc" value="%s" %s>',
GarbageCollection::DISABLED,
checked( $object->get( $post->ID ), GarbageCollection::DISABLED, false )
);
$html .= '<label for="graphql_query_skip_gc">Disable</label><br >';

echo wp_kses(
$html,
[
'input' => [
'type' => true,
'id' => true,
'name' => true,
'value' => true,
'checked' => true,
],
'br' => true,
]
);
}

}
43 changes: 43 additions & 0 deletions src/Admin/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,49 @@ function () {
]
);

register_graphql_settings_field(
'graphql_persisted_queries_section',
[
'name' => 'query_gc',
markkelnar marked this conversation as resolved.
Show resolved Hide resolved
'label' => __( 'Clean Up Queries', 'wp-graphql-smart-cache' ),
'desc' => __( 'Toggle on to enable garbage collection of saved queries older than number of days specified below', 'wp-graphql-smart-cache' ),
'type' => 'checkbox',
'default' => 'off',
'sanitize_callback' => function ( $value ) {
/**
* When enable garbage collection,
* schedule the garbage collection action/event to run once daily.
* Otherwise remove it.
*/
if ( 'on' === $value ) {
if ( ! wp_next_scheduled( 'wp_graphql_smart_cache_query_gc' ) ) {
// Add scheduled job to run in one minute
wp_schedule_event( time() + 60, 'daily', 'wp_graphql_smart_cache_query_gc' );
markkelnar marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
wp_clear_scheduled_hook( 'wp_graphql_smart_cache_query_gc' );
}
return $value;
},
]
);

register_graphql_settings_field(
'graphql_persisted_queries_section',
[
'name' => 'query_gc_age',
'desc' => __( 'Age, in number of days, of saved query when it will be removed', 'wp-graphql-smart-cache' ),
'type' => 'number',
'default' => '30',
'sanitize_callback' => function ( $value ) {
if ( 1 > $value || ! is_numeric( $value ) ) {
return function_exists( 'get_graphql_setting' ) ? \get_graphql_setting( 'query_gc_age', false, 'graphql_persisted_queries_section' ) : null;
}
return (int) $value;
},
]
);

// Add a tab section to the graphql admin settings page
register_graphql_settings_section(
'graphql_cache_section',
Expand Down
119 changes: 119 additions & 0 deletions src/Document/GarbageCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php
/**
* Content
*
* @package Wp_Graphql_Smart_Cache
*/

namespace WPGraphQL\SmartCache\Document;

use WPGraphQL\SmartCache\Admin\Settings;
use WPGraphQL\SmartCache\Document;
use GraphQL\Server\RequestError;

class GarbageCollection {

const TAXONOMY_NAME = 'graphql_document_skip_gc';
markkelnar marked this conversation as resolved.
Show resolved Hide resolved
const DISABLED = 'disabled';

public function init() {
register_taxonomy(
self::TAXONOMY_NAME,
Document::TYPE_NAME,
[
'description' => __( 'Select this option to not clean up this saved GraphQL query document after a number of days. See admin settings.', 'wp-graphql-smart-cache' ),
'labels' => [
'name' => __( 'Garbage Collection', 'wp-graphql-smart-cache' ),
],
'hierarchical' => false,
'public' => false,
'publicly_queryable' => false,
'show_admin_column' => true,
'show_in_menu' => Settings::show_in_admin(),
'show_ui' => Settings::show_in_admin(),
'show_in_quick_edit' => false,
'meta_box_cb' => [
'WPGraphQL\SmartCache\Admin\Editor',
'skip_garbage_collection_input_box_cb',
],
'show_in_graphql' => false,
// false because we register a field with different name
markkelnar marked this conversation as resolved.
Show resolved Hide resolved
]
);
}

/**
* Look up the setting for a post
*
* @param int The post id
*/
public static function get( $post_id ) {
$item = get_the_terms( $post_id, self::TAXONOMY_NAME );
return ! is_wp_error( $item ) && isset( $item[0]->name ) ? $item[0]->name : '';
}

/**
* If 'no garbage collection' is desired/selected, a value is saved as the term.
*
* @param int The post id
*/
public function disable( $post_id ) {
return wp_set_post_terms( $post_id, self::DISABLED, self::TAXONOMY_NAME );
}

/**
* If 'garbage collection' is select, the term is deleted, does not exist, which means 'allow garbage collection'.
*
* @param int The post id
*/
public function enable( $post_id ) {
$terms = wp_get_post_terms( $post_id, self::TAXONOMY_NAME );
if ( $terms ) {
foreach ( $terms as $term ) {
wp_remove_object_terms( $post_id, $term->term_id, self::TAXONOMY_NAME );
wp_delete_term( $term->term_id, self::TAXONOMY_NAME );
}
}
}

/**
* @param integer $number_of_posts Number of post ids matching criteria.
*
* @return [int] Array of post ids
*/
public static function getDocumentsByAge( $number_of_posts = 100 ) {
markkelnar marked this conversation as resolved.
Show resolved Hide resolved
// $days_ago Posts older than this many days ago
$days_ago = get_graphql_setting( 'query_gc_age', null, 'graphql_persisted_queries_section' );
if ( 1 > $days_ago || ! is_numeric( $days_ago ) ) {
return [];
}

// Query for saved query documents that are older than age and not skipping garbage collection.
// Get documents where the skip_qc taxonomy term name is not set to 'disabled'.
$wp_query = new \WP_Query(
[
'post_type' => Document::TYPE_NAME,
'post_status' => 'publish',
jasonbahl marked this conversation as resolved.
Show resolved Hide resolved
'posts_per_page' => $number_of_posts,
'fields' => 'ids',
'date_query' => [
[
'column' => 'post_modified_gmt',
'before' => $days_ago . ' days ago',
],
],
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
'tax_query' => [
[
'taxonomy' => self::TAXONOMY_NAME,
'field' => 'name',
'terms' => self::DISABLED,
'operator' => 'NOT IN',
],
],
]
);

return $wp_query->get_posts();
}
}
49 changes: 0 additions & 49 deletions tests/functional/AdminSettingsGrantCest.php

This file was deleted.