08 Nov 2026

feedDrupal.org aggregator

BloomIdea: Search API Group Access for Drupal: search and AI answers that show each member only what Group lets them open

A member of two departments asks the intranet's assistant what the company has on SMTP. The vector database ranks the whole intranet, hands back the ten closest passages, and Drupal then throws away the ones she may not open. She gets whatever is left, fewer than she asked for and not the best she could have had. On the search page next door, the same thing shows up as a result count above a list shorter than the page, and a pager that runs past the last result she can open.

That is what happens on a site built with Group 3 as soon as the search goes through Search API. Group enforces access by altering SQL queries, so Views listings of nodes respect it. A Search API search goes to its backend, where Group never sees it. The Content access processor that ships with Search API indexes node grants, and Group 3 uses none.

Search API Group Access closes that gap where it opens, inside the search backend. It is free software under GPL-2.0-or-later, and 1.0.0, released on 8 November, is its first stable release. It plugs into Search API and Group, and works on any backend that supports condition groups and IN filters: the database backend, and AI Search on a vector database such as Qdrant.

What you can build with it

  • A search page on an intranet or extranet whose result count, facets and pager agree with what each member sees, because the backend ranks and counts only that.
  • An AI assistant that answers from your content and retrieves only from the groups the person belongs to, so asking for ten passages returns ten they can open.
  • A new member who finds the group's documents on their very next search. Memberships, roles and permissions are read when the search runs, so changing them reindexes nothing.
  • Comments found by whoever may see the entity they are attached to, and reindexed when that entity changes groups, owner or status.
  • Paragraphs, or any other entity Group does not relate, that take on the access of their host through one event subscriber.
  • A large index rolled out with no gap in service: index the access values with the filter off while searches keep working, then switch the filter on without reindexing.

The same answer as Group, or no answer

The rule the module is built to is parity with Group: for the "view" operation, a search returns an item if and only if an entity query that checks access would, as far as Group decides. Outsider, insider and individual permissions, "any" and "own", published and unpublished. The test suite runs both for a matrix of accounts and compares the results, and it runs on Drupal 10 and 11.

Where it cannot be exact, it errs towards hiding. An item that has not been indexed with the processor yet is returned to nobody. There is one known case where Group would show an item and the search does not, in an item that belongs to two groups when the insider role grants less than the outsider role, and the module accepts that false negative rather than risk a false positive. Groups themselves are not covered, and neither are operations other than "view".

Caching gets the same care. Turning the processor on or off saves the index's views again, so a results cache never serves one member's results to the next.

Where it came from

We built it for our own intranet, where an assistant answers questions from about 270,000 items stored in Qdrant through AI Search, and every one of its searches goes through the filter. The person who sees least of the intranet asks for ten passages and gets ten, all of them ones they may open.

Getting there taught the module two things it now does by itself. Values meant to be matched exactly are hashed to plain hexadecimal, because AI Search runs every text attribute through an HTML to Markdown converter that escaped the underscores in them. And the status report flags an AI Search index whose access fields are not set as filterable attributes, where every search would otherwise come back empty. On Qdrant, give those fields payload indexes: on an index of 190,000 points, a filtered search took 100 to 210 ms without them and 2 to 16 ms with them.

Get it

composer require drupal/search_api_group_access
drush en search_api_group_access

Then check Group access on your index's Processors tab and reindex. It requires Drupal 10.3 or 11, Group 3.3 or later and Search API 1.39 or later. Stable releases are covered by Drupal's security advisory policy.

The README covers the rollout on a large index, AI Search and Qdrant, and the events. If you also need entity reference fields to offer only the content of the group being edited, Group Entity Reference Filter does that. And if your site shares content out by group in a way the module does not cover yet, tell us in the issue queue.

08 Nov 2026 3:59pm GMT

06 Nov 2026

feedDrupal.org aggregator

Droptica: A Drupal pipeline for turning PDFs into structured content

Person reviewing a PDF document on a laptop before moving its data into Drupal Copying facts from PDFs into a website takes time that editors could spend checking the facts themselves. To extract data from PDF into Drupal reliably, you need more than a tool that reads documents. You need a pipeline that puts each value in the right field, catches problems, and gives a person enough context to approve the result.

06 Nov 2026 7:03pm GMT

Matt Glaman: Naming my Strava runs from Garmin with a Drupal webhook

My Garmin runs sync to Strava with generic names like "Lunch Run." I built a Strava webhook in Drupal that renames them with the Garmin workout name and adds the workout plan to the description, working through it with Claude Code in one afternoon.

06 Nov 2026 6:30pm GMT

Droptica: Drupal 11.4 URLs: use core APIs instead of joining strings

Modern highway interchange with light trails on defined lanes as a metaphor for URL routes Drupal 11.4 URLs should be generated with core APIs instead of being joined from strings. This guide shows how to match the URL method to a route, entity, URI, or editor-entered path, and how to render links, check access, and keep cache metadata.

06 Nov 2026 5:46pm GMT

The Drop Times: Building Drupal Canvas Code Components with React and Tailwind CSS

An interactive pricing calculator demonstrates how front-end developers can build a component locally and give Drupal editors control over its settings.

06 Nov 2026 4:37pm GMT

Droptica: Connecting Drupal to PIM and ERP as sources of specifications

Mezzanine d'entrepôt où une équipe discute des données produit devant un poste de travail With Drupal PIM integration, PIM and ERP own approved product values while Drupal turns them into buyer answers. Define field ownership, synchronization, and outage behavior before building the integration.

06 Nov 2026 4:19pm GMT

The Drop Times: Drupal Cuts Minor-Release Security Support to Eight Months Amid Funding Strain

Organizations staying on a previous Drupal minor release will have roughly two months after its successor arrives before security coverage ends. The tighter window comes as maintainers confront rising vulnerability reports and shrinking release-management funding.

06 Nov 2026 3:32pm GMT

Droptica: A Drupal product catalog without Commerce: data first

Sales rep talking with two buyers at a trade fair stand with industrial devices on plinths A Drupal product catalog can help buyers compare specifications and contact sales without a checkout. Here's how to build it around structured Drupal records instead of PDF files.

06 Nov 2026 1:28pm GMT

Fons Vandamme: Owning up to your mistakes: my broken launch email

Owning up to my mistakes

I just sent out the Webhaven launch email and every link in it was broken.

The image that I carefully created didn't load, the "Get Webhaven" call to action button went nowhere. A disaster.

To make it even worste all the links were pointing to localhost. This is meme material. I feel ashamed about the situation but I have to accept it and live and learn.

06 Nov 2026 10:00am GMT

05 Nov 2026

feedDrupal.org aggregator

Dynamosys Insights: Drupal’s Update Window Is Getting Shorter. Is Your Maintenance Plan Ready?

Drupal’s update window is getting shorter. Calendar graphic shows ordinary minor-release security support decreasing from 12 months to 8 months.

Being on Drupal 11 doesn't tell you how long your particular release will receive security fixes. A new support policy gives website teams less time to move between minor versions. Here's what to check and how to make room for that work.

05 Nov 2026 7:36pm GMT

Talking Drupal: Talking Drupal #577 - Off The Cuff 14

Today we are talking about Drupal 12, Field UI and UX. We'll also cover DruBOM as our module of the week.

For show notes visit: https://www.talkingDrupal.com/577

Topics

Resources

Hosts

Nic Laflin - nLighteneddevelopment.com nicxvan John Picozzi - epam.com johnpicozzi Emma Horrell - emma-horrell

MOTW Correspondent

Martin Anderson-Clutz - mandclu.com mandclu

05 Nov 2026 7:00pm GMT

Giorgio Pagano: How do you let an AI agent read Drupal data without misreading it?

The Drupal drop passes a p95 chart through a read-only MCP gate to an AI assistant, which shows it as a dashboard.

Native Observability started as a way to look inside Drupal with a dashboard of its own: route timings, cache and slow queries, measured by the site and read by a person on an admin page. Now an AI agent reads the same data. Ask Claude Desktop "why is this page slow?" and it fetches the numbers through the Model Context Protocol, read-only.

Handing a model the numbers is not enough, though. It reads them with common sense, and on Drupal metrics common sense fails in specific places. In my first blind comparison, 4 answers out of 5 took a drop caused by the page cache for lost traffic, even with the right instructions in front of them.

Better instructions alone did not fix it. The site now computes the signals the model would get wrong, and explains the rest with a vocabulary checked sentence by sentence against the code. In the last round, with the vocabulary on, Claude reached the right conclusion in 30 answers out of 30.

What leaves the site follows a simpler rule than a list of forbidden words: route names go out, never a URL or a parameter value. This article covers the measurements, the mistakes I found along the way, the questions you can ask and what you need to connect an agent to your own site.

Read more

05 Nov 2026 6:35pm GMT

Fons Vandamme: Why AI builds better and faster on the shoulders of giants

Image of a person standing on a giant

The best code your AI writes is the code it never has to write.

When building on top of Drupal there is luckily a lot of that.

05 Nov 2026 5:13pm GMT

The Drop Times: Drupal's Role in Digital Sovereignty Beyond the CMS

Replacing a proprietary CMS does not automatically give governments control over their digital services. Five Government Summit speakers examine the responsibilities that come with adopting open source.

05 Nov 2026 2:28pm GMT

The Drop Times: What University Drupal Projects Reveal About the Limits of Vendor Independence

Universities can replace a CMS or reorganise their websites without knowing how difficult it would be to change providers again. The missing evidence lies in contracts, documentation and the cost of transferring technical responsibility.

05 Nov 2026 12:58pm GMT

joshics.in: Benchmarking Drupal: Mitigating Synchronous Database Locking in High-Concurrency Environments

Benchmarking Drupal: Mitigating Synchronous Database Locking in High-Concurrency Environments bhavinhjoshi

Drupal's architecture provides a highly refined routing and rendering ecosystem. When deployed behind caching layers like Varnish or Redis, serving read-heavy content is a solved problem. However, engineering mission-critical platforms, such as SLA-backed procurement portals, ticketing engines, or high-volume transaction systems, requires shifting the architectural focus from read-heavy delivery to high-concurrency write stability.

When utilizing Drupal for state management or complex relational entity saves during peak traffic, default synchronous mechanisms become a critical bottleneck.

The Core Culprit: Synchronous Key-Value Upserts

In a standard deployment, Drupal manages temporary data, system variables, and specific configurations through the State API. By default, this API utilizes the database-backed Key-Value store.

The architectural vulnerability lives directly inside \Drupal\Core\KeyValueStore\DatabaseStorage::doSet(), which handles the underlying database execution for the public set() method.

// core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php

protected function doSet($key, $value) {
  $this->connection->upsert($this->table)
    ->key(['collection', 'name'])
    ->fields([
      'collection' => $this->collection,
      'name' => $key,
      'value' => $this->serializer->encode($value),
    ])
    ->execute();
}

Every time \Drupal::state()->set() is called, it triggers a synchronous Upsert query. Under the hood, MySQL's InnoDB engine enforces row-level locks (via INSERT ... ON DUPLICATE KEY UPDATE mechanics) to maintain ACID compliance during these operations. Because the default architecture blocks the PHP thread until the database confirms the write, a severe queue forms at the database layer during concurrency spikes.

The Empirical Impact (Drupal 11 & 12 Architecture)

Architectural Note: Because the synchronous DatabaseStorage::set() mechanism and InnoDB locking architecture remain unchanged in the Drupal 12.x branch, this Drupal 11 benchmark serves as a 1:1 representation of Drupal 12's concurrency limitations.

To isolate and prove this behavior, we benchmarked a standard Drupal application handling a high-volume transactional endpoint.

  • Testing Tool: Apache JMeter.
  • Load Profile: 200 concurrent threads executing heavy writes simultaneously.
  • Operation: A synchronous \Drupal::state()->set() wrapped in a database transaction to simulate a complex payload save.

The MySQL process list immediately revealed the bottleneck. Multiple independent database connections stacked up in the process list, all attempting to execute the exact same query on the jmeter_lock_test row simultaneously. Because the first thread locked the row to perform its save, all subsequent threads were trapped waiting for it to release.


Figure 1: MySQL process list demonstrating severe thread queueing and InnoDB row locking.
MySQL terminal trace showing stacked queries in the updating state

This database queue directly correlates to a severe degradation in application performance. The application's throughput plummeted to just 4.6 requests per second. Threads queued relentlessly behind the locked row, pushing average application response times to a massive 33,310ms (33.3 seconds), with peak latency hitting 40,181ms.

Ultimately, the database connection pool saturated and began throwing lock wait timeout exceptions. The application buckled, resulting in a 29.99% failure rate where nearly a third of all transactions were dropped entirely.


Figure 2: Apache JMeter Summary Report demonstrating catastrophic latency and a 29.99% error rate.
JMeter Summary Report showing 33 second average latency and 29.99 percent error rate

The Enterprise Mitigation: Asynchronous Decoupling

To scale past this limitation and guarantee 99.99% uptime, the architecture must transition from synchronous database dependence to an asynchronous state model. In our high-concurrency deployments, we mitigate this bottleneck by decoupling the State API from MySQL entirely and offloading it to a highly available Redis cluster, combined with asynchronous message queuing for relational entity writes.

1. In-Memory State Offloading

By overriding the core state service in the application's services.yml, we reroute these synchronous database calls into memory. Redis handles tens of thousands of concurrent atomic operations per second without the locking overhead of a relational database.

# services.yml
services:
  # Override the default database-backed state service
  state:
    class: Drupal\redis\State\State
    arguments: ['@keyvalue', '@redis.client.factory', '@redis.settings']

2. Asynchronous Message Queueing

For mission-critical entity writes (e.g., bid submissions, order checkouts), we completely remove the write operation from the primary PHP thread. Instead, we serialize the payload and push it to a RabbitMQ broker via a custom service.

// High-Concurrency Transaction Controller
public function submitTransaction(Request $request) {
  $payload = $request->getContent();
  
  // 1. Acknowledge receipt instantly (0ms database lock)
  $this->rabbitMqService->publish('transaction_queue', $payload);
  
  // 2. Return 202 Accepted to the user immediately
  return new Response('Transaction queued successfully.', 202);
}

The Core Patch Proposal: Implementing DestructableInterface

While external infrastructure like Redis is standard for enterprise deployments, Drupal core should natively implement a Deferred Write-Behind Strategy within the State API to protect all environments out of the box.

By analyzing the production \Drupal\Core\State\State class (which extends CacheCollector), we can surgically remove the synchronous database bottleneck. Rather than executing $this->keyValueStore->set() immediately, the class should queue the updates in memory, allow the CacheCollector to handle the static request caching, and defer the actual database upsert() to the kernel termination phase using Drupal's native DestructableInterface.

// 1. Add the interface
use Drupal\Core\DestructableInterface;

class State extends CacheCollector implements StateInterface, DestructableInterface {

  // 2. Add the queue property
  protected array $deferredWrites = [];

  // 3. Update set() to queue the write rather than blocking the thread
  public function set($key, $value) {
    if (isset(self::$deprecatedState[$key])) {
      @trigger_error(self::$deprecatedState[$key]['message'], E_USER_DEPRECATED);
      $key = self::$deprecatedState[$key]['replacement'];
    }
    
    $this->registerKeySetDuringRequest($key, $value, parent::get($key));
    
    // Queue the database write instead of executing it synchronously
    $this->deferredWrites[$key] = $value;
    
    // Allow CacheCollector to manage the static request cache
    parent::set($key, $value);
    $this->persist($key);
    static::updateCache();
  }

  // 4. Update setMultiple() to follow the same queuing logic
  public function setMultiple(array $data) {
    foreach ($data as $key => $value) {
      $this->deferredWrites[$key] = $value;
      $this->registerKeySetDuringRequest($key, $value, parent::get($key));
      parent::set($key, $value);
      $this->persist($key);
    }
  }

  // 5. Execute the bulk database write after the HTTP response is sent
  public function destruct() {
    if (!empty($this->deferredWrites)) {
      $this->keyValueStore->setMultiple($this->deferredWrites);
      $this->deferredWrites = [];
    }
  }
}

Architectural Trade-off: Eventual Consistency
Moving writes to the KernelEvents::TERMINATE phase shifts the architecture from strict ACID consistency to Eventual Consistency. By removing the InnoDB row lock, concurrent writes rely on a "Last Write Wins" resolution at the end of the request lifecycle. Furthermore, if a PHP fatal error or server crash occurs before the termination phase, the in-memory state update will be lost despite the user receiving a successful HTTP response. While this trade-off is catastrophic for financial ledgers or strict relational entities, it is the exact right compromise for the State API, which primarily manages non-critical, self-correcting metadata.

By adopting this patch, Drupal core mathematically eliminates state-based database locking during the HTTP request. It consolidates multiple scattered state updates into a single, highly efficient setMultiple() bulk write executing safely in the background, yielding instant zero-lock latency for the end user.

The Bridge Mitigation: A Drop-in Custom Module

Until a write-behind strategy is adopted in core, engineering teams can patch this bottleneck today without requiring complex external infrastructure. By writing a lean custom module (deferred_state), we can decorate the core state service and implement the DestructableInterface ourselves, bypassing the relational lock entirely.

1. The Service Decoration (deferred_state.services.yml)

services:
  deferred_state.state:
    class: Drupal\deferred_state\State\DeferredState
    decorates: state
    arguments: ['@keyvalue']
    tags:
      - { name: needs_destruction }

2. The Asynchronous Class (src/State/DeferredState.php)

namespace Drupal\deferred_state\State;

use Drupal\Core\State\State;
use Drupal\Core\DestructableInterface;

class DeferredState extends State implements DestructableInterface {
  
  protected array $deferredWrites = [];

  public function set($key, $value) {
    // 1. Maintain CacheCollector request tracking
    $this->registerKeySetDuringRequest($key, $value, $this->get($key));
    
    // 2. Queue the write instead of calling $this->keyValueStore->set()
    $this->deferredWrites[$key] = $value;
    
    // 3. Update the underlying CacheCollector storage directly
    $this->storage[$key] = $value;
    $this->persist($key);
  }

  public function destruct() {
    if (!empty($this->deferredWrites)) {
      $this->keyValueStore->setMultiple($this->deferredWrites);
      $this->deferredWrites = [];
    }
  }
}

The Engineering Standard

Relying on page caching to scale a platform only masks the symptoms of read-heavy technical debt. True enterprise stability requires engineering the underlying infrastructure to absorb massive, concurrent writes without latency. By isolating the exact code block responsible for database locking and actively decoupling it via deferred termination events or asynchronous messaging, we mathematically eliminate the locking cascade and ensure the technology stack acts as a commercial asset, not a liability.

Request an Architectural Audit

Changed

Add new comment

05 Nov 2026 8:49am GMT