From 864e81cb1f36c93b480397393758e4340c507dcb Mon Sep 17 00:00:00 2001 From: smarcet Date: Tue, 14 Oct 2025 18:20:57 -0300 Subject: [PATCH 1/3] chore: adds APC as L1 cache at cache middleware --- app/Http/Middleware/CacheMiddleware.php | 100 ++++++++++++------ ...essScheduleEntityLifeCycleEventService.php | 1 + app/Services/Utils/RedisCacheService.php | 5 +- app/Utils/Cache/MemCache.php | 75 +++++++++++++ config/cache.php | 14 ++- 5 files changed, 157 insertions(+), 38 deletions(-) create mode 100644 app/Utils/Cache/MemCache.php diff --git a/app/Http/Middleware/CacheMiddleware.php b/app/Http/Middleware/CacheMiddleware.php index af8ac6c33..78adfd361 100644 --- a/app/Http/Middleware/CacheMiddleware.php +++ b/app/Http/Middleware/CacheMiddleware.php @@ -1,5 +1,6 @@ has($key); - Log::debug($wasHit ? "CacheMiddleware: cache HIT (tagged)" : "CacheMiddleware: cache MISS (tagged)", [ - 'tag' => $regionTag, - 'ip' => $ip, - 'agent' => $agent, - 'key' => $key, - ]); - - $encoded = Cache::tags($regionTag) - ->remember($key, $cache_lifetime, function() use ($next, $request, $regionTag, $key, $cache_lifetime, &$status,$ip, $agent) { + // try L1 APC + $encoded = MemCache::get($key); + $wasMemCacheHit = $encoded !== null; + if($wasMemCacheHit){ + Log::debug("CacheMiddleware:: MemcCache Hit"); + } + if(!$wasMemCacheHit) { + // then L2 Redis + $wasHit = Cache::tags($regionTag)->has($key); + Log::debug($wasHit ? "CacheMiddleware: cache HIT Redis (tagged)" : "CacheMiddleware: cache MISS (tagged)", [ + 'tag' => $regionTag, + 'ip' => $ip, + 'agent' => $agent, + 'key' => $key, + ]); + + $encoded = Cache::tags($regionTag) + ->remember($key, $cache_lifetime, function () use ($next, $request, $regionTag, $key, $cache_lifetime, &$status, $ip, $agent) { + $resp = $next($request); + if ($resp instanceof JsonResponse) { + $status = $resp->getStatusCode(); + if ($status === 200) { + return $this->encode($resp->getData(true)); + } + } + // don’t cache non-200 or non-JSON + return Cache::get($key); + }); + + + // backfill APC only if we actually have a value + if ($encoded !== null) { // avoid null writes + MemCache::put($key, $encoded, $cache_lifetime, $regionTag); + } + } + $data = $this->decode($encoded); + } else { + // try L1 APC + $encoded = MemCache::get($key); + $wasMemCacheHit = !is_null($encoded); + if($wasMemCacheHit){ + Log::debug("CacheMiddleware:: MemcCache Hit"); + } + if(!$wasMemCacheHit) { + // then L2 Redis + + $wasHit = Cache::has($key); + + Log::debug($wasHit ? "CacheMiddleware: cache HIT" : "CacheMiddleware: cache MISS", [ + 'ip' => $ip, + 'agent' => $agent, + 'key' => $key, + ]); + + $encoded = Cache::remember($key, $cache_lifetime, function () use ($next, $request, $key, &$status, $ip, $agent) { $resp = $next($request); if ($resp instanceof JsonResponse) { $status = $resp->getStatusCode(); - if($status === 200) { + if ($status === 200) return $this->encode($resp->getData(true)); - } } - // don’t cache non-200 or non-JSON return Cache::get($key); }); - $data = $this->decode($encoded); - } else { - $wasHit = Cache::has($key); - - Log::debug($wasHit ? "CacheMiddleware: cache HIT" : "CacheMiddleware: cache MISS", [ - 'ip' => $ip, - 'agent' => $agent, - 'key' => $key, - ]); - - $encoded = Cache::remember($key, $cache_lifetime, function() use ($next, $request, $key, &$status, $ip, $agent) { - $resp = $next($request); - if ($resp instanceof JsonResponse) { - $status = $resp->getStatusCode(); - if($status === 200) - return $this->encode($resp->getData(true)); + // store at APC + if ($encoded !== null) { // avoid null writes + MemCache::put($key, $encoded, $cache_lifetime); } - return Cache::get($key); - }); - $data = $this->decode($encoded); + + $data = $this->decode($encoded); + } } // safe guard if ($data === null) $data = is_array($encoded) ? $encoded : []; @@ -143,7 +177,7 @@ public function handle($request, Closure $next, $cache_lifetime, $cache_region = $response->headers->addCacheControlDirective('must-revalidate', true); $response->headers->addCacheControlDirective('proxy-revalidate', true); $response->headers->add([ - 'X-Cache-Result' => $wasHit ? 'HIT':'MISS', + 'X-Cache-Result' => $wasMemCacheHit ? 'HIT MemcCache' : ($wasHit ? 'HIT REDIS' : 'MISS'), ]); Log::debug( "CacheMiddleware: returning response", [ 'ip' => $ip, diff --git a/app/Services/Model/Imp/ProcessScheduleEntityLifeCycleEventService.php b/app/Services/Model/Imp/ProcessScheduleEntityLifeCycleEventService.php index 213df9108..536a55144 100644 --- a/app/Services/Model/Imp/ProcessScheduleEntityLifeCycleEventService.php +++ b/app/Services/Model/Imp/ProcessScheduleEntityLifeCycleEventService.php @@ -151,6 +151,7 @@ public function process(string $entity_operator, int $summit_id, int $entity_id, } if (!empty($cache_region_key)) { + Log::debug(sprintf("ProcessScheduleEntityLifeCycleEventService::process", ['cache_region_key' => $cache_region_key])); $this->cache_service->clearCacheRegion($cache_region_key); } diff --git a/app/Services/Utils/RedisCacheService.php b/app/Services/Utils/RedisCacheService.php index 22b9122b6..55d58e9bf 100644 --- a/app/Services/Utils/RedisCacheService.php +++ b/app/Services/Utils/RedisCacheService.php @@ -12,6 +12,7 @@ * limitations under the License. **/ +use App\Utils\Cache\MemCache; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; use libs\utils\ICacheService; @@ -331,15 +332,17 @@ public function ttl($key) return (int)$conn->ttl($key); }, 0); } - + /** * @param string $cache_region_key * @return void */ public function clearCacheRegion(string $cache_region_key): void { + Log::debug("RedisCacheService::clearCacheRegion", ["key" => $cache_region_key]); if (!empty($cache_region_key)) { Cache::tags($cache_region_key)->flush(); + MemCache::apcClearRegion($cache_region_key); if($this->exists($cache_region_key)){ Log::debug(sprintf("RedisCacheService::clearCacheRegion will clear cache region %s", $cache_region_key)); $region_data = $this->getSingleValue($cache_region_key); diff --git a/app/Utils/Cache/MemCache.php b/app/Utils/Cache/MemCache.php new file mode 100644 index 000000000..35fe8b3ac --- /dev/null +++ b/app/Utils/Cache/MemCache.php @@ -0,0 +1,75 @@ + $key]); + return self::store()->get($key, null); + } catch (\Throwable $e) { + Log::warning($e); + return null; + } + } + + + private static function trackKey(string $regionTag, string $key, int $ttl): void + { + try { + $store = self::store(); + Log::debug("MemCache::trackKey", ["regionTag" => $regionTag, "key" => $key, "ttl" => $ttl]); + $setKey = self::CM_REGION.$regionTag; + $list = $store->get($setKey, []); + if (!\is_array($list)) $list = []; + $list[$key] = \time() + $ttl; + if (\count($list) > 10000) { array_shift($list); } + $store->put($setKey, $list, $ttl); + } catch (\Throwable $e) { + Log::warning($e); + } + } + + public static function put(string $key, $value, int $ttl, ?string $regionTag = null): void + { + try { + Log::debug("MemCache::put", ["key" => $key, "value" => $value, "ttl" => $ttl]); + self::store()->put($key, $value, $ttl); + if ($regionTag) self::trackKey($regionTag, $key, $ttl); + } catch (\Throwable $e) { + Log::warning($e); + } + } + + public static function apcClearRegion(string $regionTag): int + { + try { + $store = self::store(); + $setKey = self::CM_REGION.$regionTag; + $list = $store->get($setKey, []); + $n = 0; + Log::debug("MemCache::apcClearRegion", ["regionTag" => $regionTag, "list" => $list]); + if (\is_array($list)) { + foreach (array_keys($list) as $k) { + if ($store->forget($k)) $n++; + } + } + $store->forget($setKey); + return $n; + } catch (\Throwable $e) { + Log::warning($e); + return 0; + } + } + +} diff --git a/config/cache.php b/config/cache.php index 1e63fd530..7a1eb055c 100644 --- a/config/cache.php +++ b/config/cache.php @@ -49,12 +49,18 @@ 'memcached' => [ 'driver' => 'memcached', - 'servers' => [ + //'persistent_id' => 'host-cache', + 'sasl' => [null, null], + 'servers' => [ + // UNIX socket (fastest) [ - 'host' => env('MEMCACHED_HOST', '127.0.0.1'), - 'port' => env('MEMCACHED_PORT', 11211), - 'weight' => 100, + 'host' => '/var/run/memcached/memcached.sock', + 'port' => 0, + 'weight' => 100 ], + + // or TCP if you prefer: + // ['host' => '127.0.0.1', 'port' => 11211, 'weight' => 100], ], ], From db7d65e2d19350696a93759b96b373f680642d80 Mon Sep 17 00:00:00 2001 From: smarcet Date: Tue, 14 Oct 2025 21:29:46 -0300 Subject: [PATCH 2/3] chore: add memcached to local docker --- .env.example | 4 +- .github/workflows/push.yml | 319 +++++++++++++++++---------------- Dockerfile | 4 +- app/Utils/Cache/MemCache.php | 2 +- config/cache.php | 7 +- docker-compose.yml | 338 +++++++++++++++++++---------------- 6 files changed, 359 insertions(+), 315 deletions(-) diff --git a/.env.example b/.env.example index 09fd3d1d3..886597176 100644 --- a/.env.example +++ b/.env.example @@ -251,4 +251,6 @@ OTEL_AUDIT_ELASTICSEARCH_INDEX=logs-audit L5_SWAGGER_CONST_HOST=${APP_URL} L5_SWAGGER_CONST_AUTH_URL=${IDP_AUTHORIZATION_ENDPOINT} -L5_SWAGGER_CONST_TOKEN_URL=${IDP_TOKEN_ENDPOINT} \ No newline at end of file +L5_SWAGGER_CONST_TOKEN_URL=${IDP_TOKEN_ENDPOINT} +MEMCACHED_SERVER_HOST=127.0.0.1 +MEMCACHED_SERVER_PORT=11211 diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index fb14c9f73..fba906cfd 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -7,160 +7,171 @@ on: push # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - # This workflow contains a single job called "unit-tests" - unit-tests: - # The type of runner that the job will run on - runs-on: ubuntu-latest - env: - OTEL_SERVICE_ENABLED: false - APP_ENV: testing - APP_DEBUG: true - APP_KEY: base64:4vh0op/S1dAsXKQ2bbdCfWRyCI9r8NNIdPXyZWt9PX4= - DEV_EMAIL_TO: smarcet@gmail.com - APP_URL: http://localhost - DB_CONNECTION: model - DB_HOST: 127.0.0.1 - DB_PORT: 3306 - DB_DATABASE: api_config - DB_USERNAME: root - DB_PASSWORD: 1qaz2wsx - SS_DB_HOST: 127.0.0.1 - SS_DB_PORT: 3310 - SS_DATABASE: api_model - SS_DB_USERNAME: root - SS_DB_PASSWORD: 1qaz2wsx - REDIS_HOST: 127.0.0.1 - REDIS_PORT: 6379 - REDIS_DB: 0 - REDIS_PASSWORD: 1qaz2wsx - REDIS_DATABASES: 16 - SSL_ENABLED: false - SESSION_DRIVER: redis - PHP_VERSION: 8.3 - CACHE_DRIVER: redis - SESSION_COOKIE_DOMAIN: localhost - SESSION_COOKIE_SECURE: false - QUEUE_DRIVER: redis - REGISTRATION_DEFAULT_PAYMENT_PROVIDER: Stripe - REGISTRATION_DEFAULT_STRIPE_TEST_MODE: true - REGISTRATION_DEFAULT_LIVE_STRIPE_PRIVATE_KEY: - REGISTRATION_DEFAULT_LIVE_STRIPE_PUBLISHABLE_KEY: - REGISTRATION_DEFAULT_LIVE_WEBHOOK_SECRET: - REGISTRATION_DEFAULT_TEST_STRIPE_PRIVATE_KEY: sk_test_12345 - REGISTRATION_DEFAULT_TEST_STRIPE_PUBLISHABLE_KEY: pk_12345 - REGISTRATION_DEFAULT_TEST_WEBHOOK_SECRET: whsec_12345 - BOOKABLE_ROOMS_DEFAULT_PAYMENT_PROVIDER: Stripe - BOOKABLE_ROOMS_DEFAULT_STRIPE_TEST_MODE: true - BOOKABLE_ROOMS_DEFAULT_LIVE_STRIPE_PRIVATE_KEY: - BOOKABLE_ROOMS_DEFAULT_LIVE_STRIPE_PUBLISHABLE_KEY: - BOOKABLE_ROOMS_DEFAULT_LIVE_WEBHOOK_SECRET: - BOOKABLE_ROOMS_DEFAULT_TEST_STRIPE_PRIVATE_KEY: sk_test_12345 - BOOKABLE_ROOMS_DEFAULT_TEST_STRIPE_PUBLISHABLE_KEY: pk_12345 - BOOKABLE_ROOMS_DEFAULT_TEST_WEBHOOK_SECRET: whsec_12345 - REGISTRATION_VALIDATE_TICKET_TYPE_REMOVAL: false - services: - mysql_api_model: - image: mysql:8.0 + # This workflow contains a single job called "unit-tests" + unit-tests: + # The type of runner that the job will run on + runs-on: ubuntu-latest env: - MYSQL_ROOT_PASSWORD: ${{env.SS_DB_PASSWORD}} - MYSQL_DATABASE: ${{env.SS_DATABASE}} - ports: - - 3310:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=10 --name=mysql_api_model - mysql_api_config: - image: mysql:8.0 - env: - MYSQL_ROOT_PASSWORD: ${{env.DB_PASSWORD}} - MYSQL_DATABASE: ${{env.DB_DATABASE}} - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=10 + OTEL_SERVICE_ENABLED: false + APP_ENV: testing + APP_DEBUG: true + APP_KEY: base64:4vh0op/S1dAsXKQ2bbdCfWRyCI9r8NNIdPXyZWt9PX4= + DEV_EMAIL_TO: smarcet@gmail.com + APP_URL: http://localhost + DB_CONNECTION: model + DB_HOST: 127.0.0.1 + DB_PORT: 3306 + DB_DATABASE: api_config + DB_USERNAME: root + DB_PASSWORD: 1qaz2wsx + SS_DB_HOST: 127.0.0.1 + SS_DB_PORT: 3310 + SS_DATABASE: api_model + SS_DB_USERNAME: root + SS_DB_PASSWORD: 1qaz2wsx + REDIS_HOST: 127.0.0.1 + REDIS_PORT: 6379 + REDIS_DB: 0 + REDIS_PASSWORD: 1qaz2wsx + REDIS_DATABASES: 16 + SSL_ENABLED: false + SESSION_DRIVER: redis + PHP_VERSION: 8.3 + CACHE_DRIVER: redis + SESSION_COOKIE_DOMAIN: localhost + SESSION_COOKIE_SECURE: false + QUEUE_DRIVER: redis + REGISTRATION_DEFAULT_PAYMENT_PROVIDER: Stripe + REGISTRATION_DEFAULT_STRIPE_TEST_MODE: true + REGISTRATION_DEFAULT_LIVE_STRIPE_PRIVATE_KEY: + REGISTRATION_DEFAULT_LIVE_STRIPE_PUBLISHABLE_KEY: + REGISTRATION_DEFAULT_LIVE_WEBHOOK_SECRET: + REGISTRATION_DEFAULT_TEST_STRIPE_PRIVATE_KEY: sk_test_12345 + REGISTRATION_DEFAULT_TEST_STRIPE_PUBLISHABLE_KEY: pk_12345 + REGISTRATION_DEFAULT_TEST_WEBHOOK_SECRET: whsec_12345 + BOOKABLE_ROOMS_DEFAULT_PAYMENT_PROVIDER: Stripe + BOOKABLE_ROOMS_DEFAULT_STRIPE_TEST_MODE: true + BOOKABLE_ROOMS_DEFAULT_LIVE_STRIPE_PRIVATE_KEY: + BOOKABLE_ROOMS_DEFAULT_LIVE_STRIPE_PUBLISHABLE_KEY: + BOOKABLE_ROOMS_DEFAULT_LIVE_WEBHOOK_SECRET: + BOOKABLE_ROOMS_DEFAULT_TEST_STRIPE_PRIVATE_KEY: sk_test_12345 + BOOKABLE_ROOMS_DEFAULT_TEST_STRIPE_PUBLISHABLE_KEY: pk_12345 + BOOKABLE_ROOMS_DEFAULT_TEST_WEBHOOK_SECRET: whsec_12345 + REGISTRATION_VALIDATE_TICKET_TYPE_REMOVAL: false + MEMCACHED_SERVER_HOST: 127.0.0.1 + MEMCACHED_SERVER_PORT: 11211 + services: + mysql_api_model: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: ${{env.SS_DB_PASSWORD}} + MYSQL_DATABASE: ${{env.SS_DATABASE}} + ports: + - 3310:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=10 --name=mysql_api_model + mysql_api_config: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: ${{env.DB_PASSWORD}} + MYSQL_DATABASE: ${{env.DB_DATABASE}} + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=10 - steps: - - name: Create Redis - uses: supercharge/redis-github-action@1.7.0 - with: - redis-port: ${{env.REDIS_PORT}} - redis-password: ${{env.REDIS_PASSWORD}} - # Downloads a copy of the code in your repository before running CI tests - - name: Check out repository code - uses: actions/checkout@v4 - - name: Change MYSQL sql_mode - run: > - docker exec mysql_api_model mysql -u root --password=${{env.SS_DB_PASSWORD}} -e "SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION';" - - name: Install PHP - uses: "shivammathur/setup-php@v2" - with: - php-version: ${{env.PHP_VERSION}} - extensions: pdo_mysql, mbstring, exif, pcntl, bcmath, sockets, gettext, apcu, redis, igbinary - - name: Install dependencies - uses: "ramsey/composer-install@v3" - env: - COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.COMPOSER_AUTH_TOKEN }}"} }' - - name: 'Run Tests' - run: | - ./update_doctrine.sh - php artisan db:create_test_db --schema=config - php artisan db:create_test_db --schema=model - php artisan doctrine:migrations:migrate --no-interaction --em=model_write - echo "running OAuth2SummitApiTest" - vendor/bin/phpunit --filter "OAuth2SummitApiTest" --log-junit results_summit_api_test.xml - echo "running OAuth2SummitEventsApiTest" - vendor/bin/phpunit --filter "OAuth2SummitEventsApiTest" --log-junit results_events_api_test.xml - echo "running OAuth2PresentationSubmissionTest" - vendor/bin/phpunit --filter "OAuth2PresentationSubmissionTest" --log-junit results_presentation_submissions_api_test.xml - echo "running OAuth2EventTypesApiTest" - vendor/bin/phpunit --filter "OAuth2EventTypesApiTest" --log-junit results_event_types_api_test.xml - echo "running model unit tests" - vendor/bin/phpunit tests/Unit/Entities/ --log-junit results_model_unit_tests.xml - echo "running OAuth2SummitBadgeScanApiControllerTest" - vendor/bin/phpunit --filter "OAuth2SummitBadgeScanApiControllerTest" --log-junit results_badge_scan_api_test.xml - echo "running SummitOrderService tests" - vendor/bin/phpunit --filter "SummitOrderServiceTest" --log-junit results_summitorder_service_unit_tests.xml - echo "running SummitRSVPServiceTest tests" - vendor/bin/phpunit --filter "SummitRSVPServiceTest" --log-junit results_rsvp_service_unit_tests.xml - echo "running SummitRSVPInvitationServiceTest tests" - vendor/bin/phpunit --filter "SummitRSVPInvitationServiceTest" --log-junit results_rsvp_invitation_service_unit_tests.xml - echo "running OAuth2RSVPApiTest tests" - vendor/bin/phpunit --filter "OAuth2RSVPApiTest" --log-junit results_rsvp_api_test_unit_tests.xml - echo "running OAuth2RSVPApiWithMocksTest tests" - vendor/bin/phpunit --filter "OAuth2RSVPApiWithMocksTest" --log-junit results_rsvp_api_with_mocks_test_unit_tests.xml - echo "running OAuth2RSVPInvitationApiTest tests" - vendor/bin/phpunit --filter "OAuth2RSVPInvitationApiTest" --log-junit results_rsvp_invitations_api_test_unit_tests.xml + steps: + - name: Start Memcached (with larger item size) + run: | + docker run -d --name ci-memcached -p 11211:11211 memcached:1.6-alpine \ + memcached -m 256 -I 5m -c 4096 -t 2 + # wait until ready + for i in {1..20}; do + printf "version\r\nquit\r\n" | nc 127.0.0.1 11211 >/dev/null 2>&1 && break + sleep 0.5 + done + - name: Create Redis + uses: supercharge/redis-github-action@1.7.0 + with: + redis-port: ${{env.REDIS_PORT}} + redis-password: ${{env.REDIS_PASSWORD}} + # Downloads a copy of the code in your repository before running CI tests + - name: Check out repository code + uses: actions/checkout@v4 + - name: Change MYSQL sql_mode + run: > + docker exec mysql_api_model mysql -u root --password=${{env.SS_DB_PASSWORD}} -e "SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION';" + - name: Install PHP + uses: "shivammathur/setup-php@v2" + with: + php-version: ${{env.PHP_VERSION}} + extensions: pdo_mysql, mbstring, exif, pcntl, bcmath, sockets, gettext, apcu, redis, igbinary, memcached + - name: Install dependencies + uses: "ramsey/composer-install@v3" + env: + COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.COMPOSER_AUTH_TOKEN }}"} }' + - name: 'Run Tests' + run: | + ./update_doctrine.sh + php artisan db:create_test_db --schema=config + php artisan db:create_test_db --schema=model + php artisan doctrine:migrations:migrate --no-interaction --em=model_write + echo "running OAuth2SummitApiTest" + vendor/bin/phpunit --filter "OAuth2SummitApiTest" --log-junit results_summit_api_test.xml + echo "running OAuth2SummitEventsApiTest" + vendor/bin/phpunit --filter "OAuth2SummitEventsApiTest" --log-junit results_events_api_test.xml + echo "running OAuth2PresentationSubmissionTest" + vendor/bin/phpunit --filter "OAuth2PresentationSubmissionTest" --log-junit results_presentation_submissions_api_test.xml + echo "running OAuth2EventTypesApiTest" + vendor/bin/phpunit --filter "OAuth2EventTypesApiTest" --log-junit results_event_types_api_test.xml + echo "running model unit tests" + vendor/bin/phpunit tests/Unit/Entities/ --log-junit results_model_unit_tests.xml + echo "running OAuth2SummitBadgeScanApiControllerTest" + vendor/bin/phpunit --filter "OAuth2SummitBadgeScanApiControllerTest" --log-junit results_badge_scan_api_test.xml + echo "running SummitOrderService tests" + vendor/bin/phpunit --filter "SummitOrderServiceTest" --log-junit results_summitorder_service_unit_tests.xml + echo "running SummitRSVPServiceTest tests" + vendor/bin/phpunit --filter "SummitRSVPServiceTest" --log-junit results_rsvp_service_unit_tests.xml + echo "running SummitRSVPInvitationServiceTest tests" + vendor/bin/phpunit --filter "SummitRSVPInvitationServiceTest" --log-junit results_rsvp_invitation_service_unit_tests.xml + echo "running OAuth2RSVPApiTest tests" + vendor/bin/phpunit --filter "OAuth2RSVPApiTest" --log-junit results_rsvp_api_test_unit_tests.xml + echo "running OAuth2RSVPApiWithMocksTest tests" + vendor/bin/phpunit --filter "OAuth2RSVPApiWithMocksTest" --log-junit results_rsvp_api_with_mocks_test_unit_tests.xml + echo "running OAuth2RSVPInvitationApiTest tests" + vendor/bin/phpunit --filter "OAuth2RSVPInvitationApiTest" --log-junit results_rsvp_invitations_api_test_unit_tests.xml - - name: 'Upload Unit Test Output For OAuth2SummitApiTest' - uses: actions/upload-artifact@v4 - with: - name: results_summit_api_test - path: results_summit_api_test.xml - retention-days: 5 - - name: 'Upload Unit Test Output For OAuth2SummitEventsApiTest' - uses: actions/upload-artifact@v4 - with: - name: results_events_api_test - path: results_events_api_test.xml - retention-days: 5 - - name: 'Upload Unit Test Output For OAuth2PresentationSubmissionTest' - uses: actions/upload-artifact@v4 - with: - name: results_presentation_submissions_api_test - path: results_presentation_submissions_api_test.xml - retention-days: 5 - - name: 'Upload Entity Model Unit Tests Output' - uses: actions/upload-artifact@v4 - with: - name: results_model_unit_tests - path: results_model_unit_tests.xml - retention-days: 5 - - name: 'Upload Unit Test Output For OAuth2SummitBadgeScanApiControllerTest' - uses: actions/upload-artifact@v4 - with: - name: results_badge_scan_api_test - path: results_badge_scan_api_test.xml - retention-days: 5 - - name: 'Upload Unit Test Output For SummitOrderServiceTest' - uses: actions/upload-artifact@v4 - with: - name: results_summitorder_service_unit_tests - path: results_summitorder_service_unit_tests.xml - retention-days: 5 + - name: 'Upload Unit Test Output For OAuth2SummitApiTest' + uses: actions/upload-artifact@v4 + with: + name: results_summit_api_test + path: results_summit_api_test.xml + retention-days: 5 + - name: 'Upload Unit Test Output For OAuth2SummitEventsApiTest' + uses: actions/upload-artifact@v4 + with: + name: results_events_api_test + path: results_events_api_test.xml + retention-days: 5 + - name: 'Upload Unit Test Output For OAuth2PresentationSubmissionTest' + uses: actions/upload-artifact@v4 + with: + name: results_presentation_submissions_api_test + path: results_presentation_submissions_api_test.xml + retention-days: 5 + - name: 'Upload Entity Model Unit Tests Output' + uses: actions/upload-artifact@v4 + with: + name: results_model_unit_tests + path: results_model_unit_tests.xml + retention-days: 5 + - name: 'Upload Unit Test Output For OAuth2SummitBadgeScanApiControllerTest' + uses: actions/upload-artifact@v4 + with: + name: results_badge_scan_api_test + path: results_badge_scan_api_test.xml + retention-days: 5 + - name: 'Upload Unit Test Output For SummitOrderServiceTest' + uses: actions/upload-artifact@v4 + with: + name: results_summitorder_service_unit_tests + path: results_summitorder_service_unit_tests.xml + retention-days: 5 diff --git a/Dockerfile b/Dockerfile index 49f2d7998..b9d45a7dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,7 +48,7 @@ ENV PATH=$NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/ -RUN install-php-extensions bcmath exif gettext gd imagick mbstring openssl pcntl pdo pdo_mysql sockets ${XDEBUG_VERSION} zip apcu redis igbinary +RUN install-php-extensions bcmath exif gettext gd imagick mbstring openssl pcntl pdo pdo_mysql sockets ${XDEBUG_VERSION} zip apcu redis igbinary memcached # XDEBUG COPY docker-compose/php/docker-php-ext-xdebug.ini $PHP_DIR/conf.d/docker-php-ext-xdebug.ini @@ -69,4 +69,4 @@ RUN chmod 777 -R storage # access to http://localhost:8002/apc.php to see APC statistics -RUN cd /var/www/public && curl -LO https://raw.githubusercontent.com/krakjoe/apcu/master/apc.php \ No newline at end of file +RUN cd /var/www/public && curl -LO https://raw.githubusercontent.com/krakjoe/apcu/master/apc.php diff --git a/app/Utils/Cache/MemCache.php b/app/Utils/Cache/MemCache.php index 35fe8b3ac..4ed49ccbf 100644 --- a/app/Utils/Cache/MemCache.php +++ b/app/Utils/Cache/MemCache.php @@ -5,7 +5,7 @@ final class MemCache { - private const CM_REGION = 'cm:region:'; + private const CM_REGION = 'mem:region:'; private static function store(): \Illuminate\Contracts\Cache\Repository { diff --git a/config/cache.php b/config/cache.php index 7a1eb055c..d08ad57c7 100644 --- a/config/cache.php +++ b/config/cache.php @@ -54,11 +54,10 @@ 'servers' => [ // UNIX socket (fastest) [ - 'host' => '/var/run/memcached/memcached.sock', - 'port' => 0, - 'weight' => 100 + 'host' => env('MEMCACHED_SERVER_HOST', '/var/run/memcached/memcached.sock'), + 'port' => env('MEMCACHED_SERVER_PORT',0), + 'weight' => env('MEMCACHED_SERVER_WEIGHT',100) ], - // or TCP if you prefer: // ['host' => '127.0.0.1', 'port' => 11211, 'weight' => 100], ], diff --git a/docker-compose.yml b/docker-compose.yml index bc1e5b55f..a564ef44a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,157 +1,189 @@ services: - app: - build: - context: ./ - dockerfile: Dockerfile - args: - GITHUB_OAUTH_TOKEN: ${GITHUB_OAUTH_TOKEN} - extra_hosts: - - "host.docker.internal:host-gateway" - image: summit-api - pull_policy: never - container_name: summit-api - restart: unless-stopped - working_dir: /var/www/ - volumes: - - ./:/var/www - networks: - - summit-api-local-net - depends_on: - redis: - condition: service_started - rabbitmq_sponsor_services: - condition: service_started - db_config: - condition: service_healthy - db_model: - condition: service_healthy - redis: - image: redis:latest - container_name: redis-summit-api - restart: always - command: redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD} - ports: - - ${REDIS_PORT} - volumes: - - /tmp/summit_api/redis:/root/redis - - ./docker-compose/redis/redis.conf:/usr/local/etc/redis/redis.conf - networks: - - summit-api-local-net - env_file: ./.env - rabbitmq_sponsor_services: - image: rabbitmq:3-management - environment: - RABBITMQ_DEFAULT_USER: ${DOMAIN_EVENTS_RABBITMQ_LOGIN} - RABBITMQ_DEFAULT_PASS: ${DOMAIN_EVENTS_RABBITMQ_PASSWORD} - ports: - - "15672:15672" # admin - - "5672:5672" # RabbitMQ - networks: - - summit-api-local-net - env_file: ./.env - db_config: - image: mysql:8.0 - container_name: summit-api-config-db-local - command: --default-authentication-plugin=mysql_native_password --sql_mode=NO_ENGINE_SUBSTITUTION - restart: unless-stopped - ports: - - "32780:3306" - environment: - MYSQL_DATABASE: ${DB_DATABASE} - MYSQL_PASSWORD: ${DB_PASSWORD} - MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} - SERVICE_TAGS: dev - SERVICE_NAME: mysql - healthcheck: - test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ] - timeout: 20s - retries: 10 - volumes: - - ./docker-compose/mysql/config:/docker-entrypoint-initdb.d - - mysql_summit_api_config:/var/lib/mysql - networks: - - summit-api-local-net - env_file: ./.env - db_model: - image: mysql:8.0 - container_name: summit-api-model-db-local - command: --default-authentication-plugin=mysql_native_password --sql_mode=NO_ENGINE_SUBSTITUTION - restart: unless-stopped - ports: - - "32781:3306" - environment: - MYSQL_DATABASE: ${SS_DATABASE} - MYSQL_PASSWORD: ${SS_DB_PASSWORD} - MYSQL_ROOT_PASSWORD: ${SS_DB_PASSWORD} - SERVICE_TAGS: dev - SERVICE_NAME: mysql - healthcheck: - test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ] - timeout: 20s - retries: 10 - volumes: - - ./docker-compose/mysql/model:/docker-entrypoint-initdb.d - - mysql_summit_api_model:/var/lib/mysql - networks: - - summit-api-local-net - env_file: ./.env - nginx: - image: nginx:alpine - container_name: nginx-summit-api - restart: unless-stopped - ports: - - "8002:80" - volumes: - - ./:/var/www - - ./docker-compose/nginx:/etc/nginx/conf.d/ - networks: - - summit-api-local-net - env_file: ./.env - depends_on: - - app - otel-collector: - image: otel/opentelemetry-collector-contrib - volumes: - - ./docker-compose/opentelemetry/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml - - ./docker-compose/opentelemetry/otel-traces.json:/tmp/otel-traces.json - ports: - - 1888:1888 # pprof extension - - 8888:8888 # Prometheus metrics exposed by the Collector - - 8889:8889 # Prometheus exporter metrics - - 13133:13133 # health_check extension - - 4317:4317 # OTLP gRPC receiver - - 4318:4318 # OTLP http receiver - - 55679:55679 # zpages extension - networks: - - summit-api-local-net - depends_on: - - elasticsearch + app: + build: + context: ./ + dockerfile: Dockerfile + args: + GITHUB_OAUTH_TOKEN: ${GITHUB_OAUTH_TOKEN} + extra_hosts: + - "host.docker.internal:host-gateway" + image: summit-api + pull_policy: never + container_name: summit-api + restart: unless-stopped + working_dir: /var/www/ + volumes: + - ./:/var/www + networks: + - summit-api-local-net + depends_on: + redis: + condition: service_started + rabbitmq_sponsor_services: + condition: service_started + db_config: + condition: service_healthy + db_model: + condition: service_healthy + memcached: + condition: service_healthy + + memcached: + image: memcached:1.6-alpine + container_name: memcached-summit-api + restart: unless-stopped + command: + - memcached + - -m + - "256" # memory (MB) + - -I + - "5m" # per-item limit + - -c + - "4096" # max connections + - -t + - "2" # threads + healthcheck: + test: ["CMD", "sh", "-lc", "printf 'version\r\n' | nc -w 1 127.0.0.1 11211 >/dev/null"] + interval: 10s + timeout: 3s + retries: 5 + networks: + - summit-api-local-net + + redis: + image: redis:latest + container_name: redis-summit-api + restart: always + command: redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD} + ports: + - ${REDIS_PORT} + volumes: + - /tmp/summit_api/redis:/root/redis + - ./docker-compose/redis/redis.conf:/usr/local/etc/redis/redis.conf + networks: + - summit-api-local-net + env_file: ./.env + + rabbitmq_sponsor_services: + image: rabbitmq:3-management + environment: + RABBITMQ_DEFAULT_USER: ${DOMAIN_EVENTS_RABBITMQ_LOGIN} + RABBITMQ_DEFAULT_PASS: ${DOMAIN_EVENTS_RABBITMQ_PASSWORD} + ports: + - "15672:15672" # admin + - "5672:5672" # RabbitMQ + networks: + - summit-api-local-net + env_file: ./.env + + db_config: + image: mysql:8.0 + container_name: summit-api-config-db-local + command: --default-authentication-plugin=mysql_native_password --sql_mode=NO_ENGINE_SUBSTITUTION + restart: unless-stopped + ports: + - "32780:3306" + environment: + MYSQL_DATABASE: ${DB_DATABASE} + MYSQL_PASSWORD: ${DB_PASSWORD} + MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} + SERVICE_TAGS: dev + SERVICE_NAME: mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + timeout: 20s + retries: 10 + volumes: + - ./docker-compose/mysql/config:/docker-entrypoint-initdb.d + - mysql_summit_api_config:/var/lib/mysql + networks: + - summit-api-local-net + env_file: ./.env + + db_model: + image: mysql:8.0 + container_name: summit-api-model-db-local + command: --default-authentication-plugin=mysql_native_password --sql_mode=NO_ENGINE_SUBSTITUTION + restart: unless-stopped + ports: + - "32781:3306" + environment: + MYSQL_DATABASE: ${SS_DATABASE} + MYSQL_PASSWORD: ${SS_DB_PASSWORD} + MYSQL_ROOT_PASSWORD: ${SS_DB_PASSWORD} + SERVICE_TAGS: dev + SERVICE_NAME: mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + timeout: 20s + retries: 10 + volumes: + - ./docker-compose/mysql/model:/docker-entrypoint-initdb.d + - mysql_summit_api_model:/var/lib/mysql + networks: + - summit-api-local-net + env_file: ./.env + + nginx: + image: nginx:alpine + container_name: nginx-summit-api + restart: unless-stopped + ports: + - "8002:80" + volumes: + - ./:/var/www + - ./docker-compose/nginx:/etc/nginx/conf.d/ + networks: + - summit-api-local-net + env_file: ./.env + depends_on: + - app + + otel-collector: + image: otel/opentelemetry-collector-contrib + volumes: + - ./docker-compose/opentelemetry/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml + - ./docker-compose/opentelemetry/otel-traces.json:/tmp/otel-traces.json + ports: + - 1888:1888 + - 8888:8888 + - 8889:8889 + - 13133:13133 + - 4317:4317 + - 4318:4318 + - 55679:55679 + networks: + - summit-api-local-net + depends_on: + - elasticsearch + + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0 + container_name: elasticsearch + environment: + - discovery.type=single-node + - xpack.security.enabled=false + - ES_JAVA_OPTS=-Xms512m -Xmx512m + - cluster.name=summit-api-cluster + - node.name=summit-api-node + ports: + - "9200:9200" + - "9300:9300" + volumes: + - elasticsearch_data:/usr/share/elasticsearch/data + networks: + - summit-api-local-net + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"] + interval: 30s + timeout: 10s + retries: 5 - elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0 - container_name: elasticsearch - environment: - - discovery.type=single-node - - xpack.security.enabled=false - - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - - cluster.name=summit-api-cluster - - node.name=summit-api-node - ports: - - "9200:9200" - - "9300:9300" - volumes: - - elasticsearch_data:/usr/share/elasticsearch/data - networks: - - summit-api-local-net - healthcheck: - test: [ "CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1" ] - interval: 30s - timeout: 10s - retries: 5 networks: - summit-api-local-net: - driver: bridge + summit-api-local-net: + driver: bridge + volumes: - mysql_summit_api_model: - mysql_summit_api_config: - elasticsearch_data: + mysql_summit_api_model: + mysql_summit_api_config: + elasticsearch_data: From 99cd13d72c21931872317966dafc7c4d163e7f2c Mon Sep 17 00:00:00 2001 From: sebastian marcet Date: Tue, 14 Oct 2025 21:33:51 -0300 Subject: [PATCH 3/3] chore: Update app/Services/Model/Imp/ProcessScheduleEntityLifeCycleEventService.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Model/Imp/ProcessScheduleEntityLifeCycleEventService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Services/Model/Imp/ProcessScheduleEntityLifeCycleEventService.php b/app/Services/Model/Imp/ProcessScheduleEntityLifeCycleEventService.php index 536a55144..2a342ad30 100644 --- a/app/Services/Model/Imp/ProcessScheduleEntityLifeCycleEventService.php +++ b/app/Services/Model/Imp/ProcessScheduleEntityLifeCycleEventService.php @@ -151,7 +151,7 @@ public function process(string $entity_operator, int $summit_id, int $entity_id, } if (!empty($cache_region_key)) { - Log::debug(sprintf("ProcessScheduleEntityLifeCycleEventService::process", ['cache_region_key' => $cache_region_key])); + Log::debug("ProcessScheduleEntityLifeCycleEventService::process", ['cache_region_key' => $cache_region_key]); $this->cache_service->clearCacheRegion($cache_region_key); }