This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-website.git
The following commit(s) were added to refs/heads/master by this push:
new 56b5f5a3c96 [feature](website) Turn /slack into a Matomo tracking page
with UTM attribution (#4101)
56b5f5a3c96 is described below
commit 56b5f5a3c963451fa1329a9d00648a8574d4668f
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Tue Sep 1 10:10:23 2026 +0800
[feature](website) Turn /slack into a Matomo tracking page with UTM
attribution (#4101)
## What problem does this PR solve?
We want to know where new Slack members discover Apache Doris (website,
docs, GitHub, LinkedIn, events, third-party posts, ...), but
`doris.apache.org/slack` was a plain client-side redirect, so every
click landed in Slack with no attribution at all.
## Solution
Turn `https://doris.apache.org/slack` from a static redirect into a
lightweight tracking page that records one event in the ASF Matomo
instance this site already uses, then forwards the visitor to the real
Slack invite. No backend, no new third-party analytics, and the public
entry URL stays the same for every channel.
### The `/slack` page (`src/pages/slack.tsx` +
`src/components/slack-redirect/`)
- Reads `utm_source / utm_medium / utm_campaign / utm_content` and
`document.referrer`, resolves attribution with priority **UTM > referrer
> direct**, and reports a single Matomo event: `Category="Community
Acquisition"`, `Action="Slack Click"`,
`Name=source|medium|campaign|content` — e.g.
`github|community||github_readme`, `reddit.com|referral||`,
`direct|none||`.
- Values are sanitised (lowercase, `[a-z0-9._-]` only, 64 chars max), so
the `|` separator can never leak into a field.
- Redirect timing: the event is queued with a completion callback and
sent via `navigator.sendBeacon`, so the page leaves as soon as the
request is handed to the browser (~0.9s when the tracker is up;
matomo.js holds every request after the automatic pageview for 800ms).
Resource Timing distinguishes a tracker that failed to load (ad blocker
/ 404 → leave immediately) from one still downloading (wait up to 1.5s),
with a 2.5s hard cap. No-JS visitors are forwarded by a `<noscript>`
meta refresh. The page is `noindex` and excluded from the sitemap.
- The real invite URL now lives in exactly one place:
`customFields.slackInviteUrl` in `docusaurus.config.js`. Rotating the
invite no longer touches any content.
### In-site Slack entry points now carry UTM parameters
| Entry | `utm_medium` / `utm_content` |
|---|---|
| Homepage hero "Join Slack" | `website` / `homepage_hero` |
| Homepage community section | `website` / `homepage_community` |
| Footer icon | `website` / `footer` |
| Docs TOC "Chat on Slack" | `docs` / `docs_toc` |
| Mascot nudge (all pages) | `docs` on docs/community pages, else
`website` / `mascot_nudge` |
| Community docs (en + zh-CN) | `docs` / `community_join_page`,
`contribute_guide` |
| 44 blog posts, 48 links | `content` / `blog_<post-file-name>` |
| Release notes 3.0.0 | `docs` / `release_notes_3_0_0` |
React components generate these URLs through a shared
`buildSlackEntryUrl()` helper instead of hand-written strings; links
stay absolute so they keep opening in a new tab as before. One stale
direct `join.slack.com` invite link in a blog post was replaced as well.
The naming convention for external channels (GitHub README, LinkedIn,
webinars, conference QR codes, ...) is documented in
`developer_docs/slack-utm-convention.md`.
## Check List
- `node --test
src/components/slack-redirect/slack-attribution.logic.test.js`: 12/12
pass.
- `tsc` over all touched TS/TSX files: clean.
- Production build spot check: `/slack/` and `/zh-CN/slack/` emit the
noscript fallback, `noindex`, and the Matomo bootstrap; the sitemap no
longer lists `/slack`; `plugin-client-redirects` no longer manages the
route.
- Headless-browser verification (dev server + the exact Matomo bootstrap
the plugin injects in production, with `matomo.php` answered locally so
no test data reached ASF): correct event payloads and redirect timing
for tracker present / slow / blocked / 404 / absent, referrer fallback
(`reddit.com|referral||`), value sanitisation, and every homepage entry
point carrying its UTM parameters.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01SWLsJt9S3m6ExTP4i9waMG
Co-authored-by: morningman <[email protected]>
Co-authored-by: Claude Fable 5 <[email protected]>
---
...f-an-Entire-MySQL-Database-for-Data-Analysis.md | 2 +-
...Times-More-Cost-Effective-Than-Elasticsearch.md | 2 +-
...ng-a-Data-Warehouse-for-Traditional-Industry.md | 2 +-
...xt-Generation-Data-Lakehouse-10X-Performance.md | 2 +-
...ection-How-Fast-Data-Queries-Are-Implemented.md | 2 +-
...n-thousand-Dashboards-Without-Causing-a-Mess.md | 2 +-
...o-Build-a-Simple-but-Solid-Data-Architecture.md | 2 +-
...k-the-Data-Update-Mechanism-of-Your-Database.md | 2 +-
...-to-That-Poor-BI-Engineer-We-Need-Fast-Joins.md | 2 +-
...Per-Day-and-Keep-Big-Queries-Within-1-Second.md | 2 +-
...asticsearch-and-PostgreSQL-with-Apache-Doris.md | 4 +-
blog/Say-Goodbye-to-OOM-Crashes.md | 2 +-
...-Why-We-Went-from-ClickHouse-to-Apache-Doris.md | 2 +-
...orage-for-Hot-and-Cold-Data-What-Why-and-How.md | 2 +-
blog/Understanding-Data-Compaction-in-3-Minutes.md | 2 +-
...al-time-data-warehouse-based-on-apache-doris.md | 2 +-
...ion-based-on-the-apache-doris-data-warehouse.md | 2 +-
...d-built-a-pb-scale-logging-system-with-doris.md | 2 +-
...log-and-time-series-data-analysis-in-netease.md | 4 +-
...ta-reporting-tagging-and-data-lake-analytics.md | 2 +-
...expect-from-apache-doris-as-a-data-warehouse.md | 2 +-
...in-apache-doris-for-10x-faster-data-transfer.md | 2 +-
blog/auto-increment-columns-in-databases.md | 2 +-
blog/auto-partition-in-apache-doris.md | 2 +-
...wn-data-silos-with-an-apache-doris-based-cdp.md | 2 +-
blog/cross-cluster-replication-for-read-write.md | 2 +-
...appens-in-real-time-is-analyzed-in-real-time.md | 2 +-
...-by-enabling-seven-times-faster-log-analysis.md | 2 +-
...olution-of-the-apache-doris-execution-engine.md | 2 +-
...he-doris-upgrading-an-observability-platform.md | 4 +-
...e-iov-data-analytics-helps-prevent-accidents.md | 2 +-
...s-a-next-generation-real-time-data-warehouse.md | 2 +-
...erates-text-searches-by-40-time-apache-doris.md | 2 +-
blog/job-scheduler-for-task-automation.md | 2 +-
blog/log-analysis-elasticsearch-vs-apache-doris.md | 2 +-
blog/migrate-lakehouse-from-bigquery-to-doris.md | 4 +-
...rom-clickhouse-to-apache-doris-what-happened.md | 2 +-
...ti-tenant-workload-isolation-in-apache-doris.md | 2 +-
blog/release-note-2.0.0.md | 2 +-
blog/release-note-2.1.0.md | 2 +-
blog/release-note-3.0.0.md | 2 +-
...tencent-music-migrate-elasticsearch-to-doris.md | 2 +-
blog/variant-in-apache-doris-2.1.md | 2 +-
...is-best-alternatives-for-real-time-analytics.md | 2 +-
.../how-to-contribute/contribute-to-doris.mdx | 2 +-
community/join-community.md | 4 +-
developer_docs/slack-utm-convention.md | 105 +++++++++++++
docusaurus.config.js | 19 ++-
.../how-to-contribute/contribute-to-doris.mdx | 2 +-
.../current/join-community.md | 4 +-
releasenotes/v3.0/release-3.0.0.md | 2 +-
src/components/home-next/SlackCommunityNudge.tsx | 8 +-
.../home-next/sections/CommunitySection.tsx | 3 +-
src/components/home-next/sections/HeroSection.tsx | 3 +-
src/components/slack-redirect/SlackRedirect.scss | 109 +++++++++++++
src/components/slack-redirect/SlackRedirect.tsx | 164 ++++++++++++++++++++
.../slack-redirect/slack-attribution.logic.d.ts | 64 ++++++++
.../slack-redirect/slack-attribution.logic.js | 170 +++++++++++++++++++++
.../slack-redirect/slack-attribution.logic.test.js | 136 +++++++++++++++++
src/pages/slack.tsx | 10 ++
src/theme/Footer/index.tsx | 3 +-
src/theme/TOC/index.tsx | 5 +-
62 files changed, 841 insertions(+), 68 deletions(-)
diff --git
a/blog/Auto-Synchronization-of-an-Entire-MySQL-Database-for-Data-Analysis.md
b/blog/Auto-Synchronization-of-an-Entire-MySQL-Database-for-Data-Analysis.md
index b9be94dcfb8..964adf9ec67 100644
--- a/blog/Auto-Synchronization-of-an-Entire-MySQL-Database-for-Data-Analysis.md
+++ b/blog/Auto-Synchronization-of-an-Entire-MySQL-Database-for-Data-Analysis.md
@@ -191,5 +191,5 @@ CREATE TABLE doris_sink (
);
```
-If you've got any questions, find Apache Doris developers on
[Slack](https://doris.apache.org/slack).
+If you've got any questions, find Apache Doris developers on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_auto-synchronization-of-an-entire-mysql-database-for-data-a).
diff --git
a/blog/Building-A-Log-Analytics-Solution-10-Times-More-Cost-Effective-Than-Elasticsearch.md
b/blog/Building-A-Log-Analytics-Solution-10-Times-More-Cost-Effective-Than-Elasticsearch.md
index 208af91b3d7..2ed4855cb56 100644
---
a/blog/Building-A-Log-Analytics-Solution-10-Times-More-Cost-Effective-Than-Elasticsearch.md
+++
b/blog/Building-A-Log-Analytics-Solution-10-Times-More-Cost-Effective-Than-Elasticsearch.md
@@ -247,4 +247,4 @@ For more feature introduction and usage guide, see
documentation: [Inverted Inde
In a word, what contributes to Apache Doris' 10-time higher cost-effectiveness
than Elasticsearch is its OLAP-tailored optimizations for inverted indexing,
supported by the columnar storage engine, massively parallel processing
framework, vectorized query engine, and cost-based optimizer of Apache Doris.
-As proud as we are about our own inverted indexing solution, we understand
that self-published benchmarks can be controversial, so we are open to
[feedback](https://doris.apache.org/slack) from any third-party users and see
how [Apache Doris](https://github.com/apache/doris) works in real-world cases.
+As proud as we are about our own inverted indexing solution, we understand
that self-published benchmarks can be controversial, so we are open to
[feedback](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_building-a-log-analytics-solution-10-times-more-cost-effect)
from any third-party users and see how [Apache
Doris](https://github.com/apache/doris) works in real-world cases.
diff --git a/blog/Building-a-Data-Warehouse-for-Traditional-Industry.md
b/blog/Building-a-Data-Warehouse-for-Traditional-Industry.md
index d019c168b60..926762260c1 100644
--- a/blog/Building-a-Data-Warehouse-for-Traditional-Industry.md
+++ b/blog/Building-a-Data-Warehouse-for-Traditional-Industry.md
@@ -172,4 +172,4 @@ Actually, before we evolved into our current data
architecture, we tried Hive, S
On the other hand, to smoothen our big data transition, we need to make our
data platform as simple as possible in terms of usage and maintenance. That's
why we landed on Apache Doris. It is compatible with MySQL protocol and
provides a rich collection of functions so we don't have to develop our own
UDFs. Also, it is composed of only two types of processes: frontends and
backends, so it is easy to scale and track.
-Find Apache Doris developers on [Slack](https://doris.apache.org/slack).
+Find Apache Doris developers on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_building-a-data-warehouse-for-traditional-industry).
diff --git
a/blog/Building-the-Next-Generation-Data-Lakehouse-10X-Performance.md
b/blog/Building-the-Next-Generation-Data-Lakehouse-10X-Performance.md
index 67beab10718..1b887b1ac8c 100644
--- a/blog/Building-the-Next-Generation-Data-Lakehouse-10X-Performance.md
+++ b/blog/Building-the-Next-Generation-Data-Lakehouse-10X-Performance.md
@@ -249,5 +249,5 @@ http://doris.apache.org
https://github.com/apache/doris
-Find Apache Doris developers on [Slack](https://doris.apache.org/slack).
+Find Apache Doris developers on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_building-the-next-generation-data-lakehouse-10x-performance).
diff --git a/blog/Database-Dissection-How-Fast-Data-Queries-Are-Implemented.md
b/blog/Database-Dissection-How-Fast-Data-Queries-Are-Implemented.md
index 38a5b69dc18..7eae5ee188b 100644
--- a/blog/Database-Dissection-How-Fast-Data-Queries-Are-Implemented.md
+++ b/blog/Database-Dissection-How-Fast-Data-Queries-Are-Implemented.md
@@ -134,7 +134,7 @@ The results are as below:
In short, what contributed to the fast data loading and data queries in this
case?
- The Colocate mechanism that's designed for distributed computing
-- Collaboration between database users and
[developers](https://doris.apache.org/slack) that enables the operator merging
+- Collaboration between database users and
[developers](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_database-dissection-how-fast-data-queries-are-implemented)
that enables the operator merging
- Support for a wide range of data loading methods to choose from
- A vectorized engine that brings overall performance increase
diff --git
a/blog/Database-in-Fintech-How-to-Support-ten-thousand-Dashboards-Without-Causing-a-Mess.md
b/blog/Database-in-Fintech-How-to-Support-ten-thousand-Dashboards-Without-Causing-a-Mess.md
index 0d81adde3b5..074e1c2b268 100644
---
a/blog/Database-in-Fintech-How-to-Support-ten-thousand-Dashboards-Without-Causing-a-Mess.md
+++
b/blog/Database-in-Fintech-How-to-Support-ten-thousand-Dashboards-Without-Causing-a-Mess.md
@@ -73,7 +73,7 @@ Analysts often check data reports of the same metrics on a
regular basis. These
The complexity of data analysis in the financial industry lies in the data
itself other than the engineering side. Thus, the underlying data architecture
should focus on facilitating the unified and efficient management of data.
Apache Doris provides the flexibility of simple metric registration and the
ability of fast and resource-efficient metric computation. In this case, the
user is able to handle 10,000 active financial metrics in 10,000 dashboards
with 30% less ETL efforts.
-Find Apache Doris developers on [Slack](https://doris.apache.org/slack).
+Find Apache Doris developers on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_database-in-fintech-how-to-support-ten-thousand-dashboards).
diff --git
a/blog/For-Entry-Level-Data-Engineers-How-to-Build-a-Simple-but-Solid-Data-Architecture.md
b/blog/For-Entry-Level-Data-Engineers-How-to-Build-a-Simple-but-Solid-Data-Architecture.md
index 24ed3dbe120..75394ba857d 100644
---
a/blog/For-Entry-Level-Data-Engineers-How-to-Build-a-Simple-but-Solid-Data-Architecture.md
+++
b/blog/For-Entry-Level-Data-Engineers-How-to-Build-a-Simple-but-Solid-Data-Architecture.md
@@ -100,4 +100,4 @@ Firstly, Apache Doris is famously fast in Join queries. So
if you need to extrac
## Conclusion
-This is the overview of a simple data architecture and how it can provide the
data services you need. It ensures data ingestion stability and quality with
Flink CDC, and quick data analysis with Apache Doris. The deployment of this
architecture is simple, too. If you plan for a data analytic upgrade for your
business, you might refer to this case. If you need advice and help, you may
join our [community here](https://doris.apache.org/slack).
+This is the overview of a simple data architecture and how it can provide the
data services you need. It ensures data ingestion stability and quality with
Flink CDC, and quick data analysis with Apache Doris. The deployment of this
architecture is simple, too. If you plan for a data analytic upgrade for your
business, you might refer to this case. If you need advice and help, you may
join our [community
here](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_conten
[...]
diff --git
a/blog/Is-Your-Latest-Data-Really-the-Latest-Check-the-Data-Update-Mechanism-of-Your-Database.md
b/blog/Is-Your-Latest-Data-Really-the-Latest-Check-the-Data-Update-Mechanism-of-Your-Database.md
index 608482a4275..2e434c9d245 100644
---
a/blog/Is-Your-Latest-Data-Really-the-Latest-Check-the-Data-Update-Mechanism-of-Your-Database.md
+++
b/blog/Is-Your-Latest-Data-Really-the-Latest-Check-the-Data-Update-Mechanism-of-Your-Database.md
@@ -223,5 +223,5 @@ mysql> select * from test_table;
## Conclusion
-Congratulations. Now you've gained an overview of how data updates are
implemented in Apache Doris. With this knowledge, you can basically guarantee
efficiency and accuracy of data updating. But wait, there is so much more about
that. As Apache Doris 2.0 is going to provide more powerful Partial Column
Update capabilities, with improved execution of the Update statement and the
support for more complicated multi-table Join queries, I will show you how to
take advantage of them in details [...]
+Congratulations. Now you've gained an overview of how data updates are
implemented in Apache Doris. With this knowledge, you can basically guarantee
efficiency and accuracy of data updating. But wait, there is so much more about
that. As Apache Doris 2.0 is going to provide more powerful Partial Column
Update capabilities, with improved execution of the Update statement and the
support for more complicated multi-table Join queries, I will show you how to
take advantage of them in details [...]
diff --git a/blog/Listen-to-That-Poor-BI-Engineer-We-Need-Fast-Joins.md
b/blog/Listen-to-That-Poor-BI-Engineer-We-Need-Fast-Joins.md
index 641201f8aeb..e1e2b987de8 100644
--- a/blog/Listen-to-That-Poor-BI-Engineer-We-Need-Fast-Joins.md
+++ b/blog/Listen-to-That-Poor-BI-Engineer-We-Need-Fast-Joins.md
@@ -102,4 +102,4 @@ We believe self-service BI is the future in the BI
landscape, just like AGI is t
-Find the Apache Doris developers on [Slack](https://doris.apache.org/slack)
+Find the Apache Doris developers on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_listen-to-that-poor-bi-engineer-we-need-fast-joins)
diff --git
a/blog/Log-Analysis-How-to-Digest-15-Billion-Logs-Per-Day-and-Keep-Big-Queries-Within-1-Second.md
b/blog/Log-Analysis-How-to-Digest-15-Billion-Logs-Per-Day-and-Keep-Big-Queries-Within-1-Second.md
index 3a03e0b787a..38dd05825e3 100644
---
a/blog/Log-Analysis-How-to-Digest-15-Billion-Logs-Per-Day-and-Keep-Big-Queries-Within-1-Second.md
+++
b/blog/Log-Analysis-How-to-Digest-15-Billion-Logs-Per-Day-and-Keep-Big-Queries-Within-1-Second.md
@@ -88,7 +88,7 @@ These strategies have shortened the response time of queries.
For example, a que
## Ongoing Plans
-The user is now testing with the newly added [inverted
index](https://doris.apache.org/docs/table-design/index/inverted-index) in
Apache Doris. It is designed to speed up full-text search of strings as well as
equivalence and range queries of numerics and datetime. They have also provided
their valuable feedback about the auto-bucketing logic in Doris: Currently,
Doris decides the number of buckets for a partition based on the data size of
the previous partition. The problem for the use [...]
+The user is now testing with the newly added [inverted
index](https://doris.apache.org/docs/table-design/index/inverted-index) in
Apache Doris. It is designed to speed up full-text search of strings as well as
equivalence and range queries of numerics and datetime. They have also provided
their valuable feedback about the auto-bucketing logic in Doris: Currently,
Doris decides the number of buckets for a partition based on the data size of
the previous partition. The problem for the use [...]
diff --git
a/blog/Replacing-Apache-Hive-Elasticsearch-and-PostgreSQL-with-Apache-Doris.md
b/blog/Replacing-Apache-Hive-Elasticsearch-and-PostgreSQL-with-Apache-Doris.md
index a759bdad044..fe9bec3f35d 100644
---
a/blog/Replacing-Apache-Hive-Elasticsearch-and-PostgreSQL-with-Apache-Doris.md
+++
b/blog/Replacing-Apache-Hive-Elasticsearch-and-PostgreSQL-with-Apache-Doris.md
@@ -114,11 +114,11 @@ We have 2 clusters in Apache Doris accommodating tens of
TBs of data, with almos

-Lastly, I would like to share with you something that interested us most when
we first talked to the [Apache Doris community](https://doris.apache.org/slack):
+Lastly, I would like to share with you something that interested us most when
we first talked to the [Apache Doris
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_replacing-apache-hive-elasticsearch-and-postgresql-with-apa):
- Apache Doris supports data ingestion transactions so it can ensure data is
written **exactly once**.
- It is well-integrated with the data ecosystem and can smoothly interface
with most data sources and data formats.
- It allows us to implement elastic scaling of clusters using the command line
interface.
- It outperforms ClickHouse in **join queries**.
-Find Apache Doris developers on
[Slack](https://join.slack.com/t/apachedoriscommunity/shared_invite/zt-1t3wfymur-0soNPATWQ~gbU8xutFOLog)
+Find Apache Doris developers on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_replacing-apache-hive-elasticsearch-and-postgresql-with-apa)
diff --git a/blog/Say-Goodbye-to-OOM-Crashes.md
b/blog/Say-Goodbye-to-OOM-Crashes.md
index 86c28480d6a..13df247f8ec 100644
--- a/blog/Say-Goodbye-to-OOM-Crashes.md
+++ b/blog/Say-Goodbye-to-OOM-Crashes.md
@@ -163,5 +163,5 @@ If the process memory consumed is beyond the MemLimit (90%
of total system memor
After optimizations in memory allocation, memory tracking, and memory limit,
we have substantially increased the stability and high-concurrency performance
of Apache Doris as a real-time analytic data warehouse platform. OOM crash in
the backend is a rare scene now. Even if there is an OOM, users can locate the
problem root based on the logs and then fix it. In addition, with more flexible
memory limits on queries and data ingestion, users don't have to spend extra
effort taking care of [...]
-In the next phase, we plan to ensure completion of queries in memory
overcommitment, which means less queries will have to be canceled due to memory
shortage. We have broken this objective into specific directions of work:
exception safety, memory isolation between resource groups, and the flushing
mechanism of intermediate data. If you want to meet our developers, [this is
where you find us](https://doris.apache.org/slack).
+In the next phase, we plan to ensure completion of queries in memory
overcommitment, which means less queries will have to be canceled due to memory
shortage. We have broken this objective into specific directions of work:
exception safety, memory isolation between resource groups, and the flushing
mechanism of intermediate data. If you want to meet our developers, [this is
where you find
us](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_say-goodby
[...]
diff --git
a/blog/Tencent-Data-Engineers-Why-We-Went-from-ClickHouse-to-Apache-Doris.md
b/blog/Tencent-Data-Engineers-Why-We-Went-from-ClickHouse-to-Apache-Doris.md
index be4cbba171a..001f26f53a8 100644
--- a/blog/Tencent-Data-Engineers-Why-We-Went-from-ClickHouse-to-Apache-Doris.md
+++ b/blog/Tencent-Data-Engineers-Why-We-Went-from-ClickHouse-to-Apache-Doris.md
@@ -255,4 +255,4 @@ http://doris.apache.org
https://github.com/apache/doris
-Find Apache Doris developers on [Slack](https://doris.apache.org/slack)
+Find Apache Doris developers on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_tencent-data-engineers-why-we-went-from-clickhouse-to-apach)
diff --git a/blog/Tiered-Storage-for-Hot-and-Cold-Data-What-Why-and-How.md
b/blog/Tiered-Storage-for-Hot-and-Cold-Data-What-Why-and-How.md
index c4d4d8405d4..e3e1e6d18de 100644
--- a/blog/Tiered-Storage-for-Hot-and-Cold-Data-What-Why-and-How.md
+++ b/blog/Tiered-Storage-for-Hot-and-Cold-Data-What-Why-and-How.md
@@ -353,7 +353,7 @@ Apache Doris 2.0 has been optimized for cold data queries.
Only the first-time a
In Apache Doris, each data ingestion leads to the generation of a new Rowset,
so the update of historical data will be put in a Rowset that is separated from
those of newly loaded data. That’s how it makes sure the updating of cold data
does not interfere with the ingestion of hot data. Once the rowsets cool down,
they will be moved to S3 and deleted locally, and the updated historical data
will go to the partition where it belongs.
-If you any questions, come find Apache Doris developers on
[Slack](https://doris.apache.org/slack). We will be happy to provide targeted
support.
+If you any questions, come find Apache Doris developers on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_tiered-storage-for-hot-and-cold-data-what-why-and-how).
We will be happy to provide targeted support.
diff --git a/blog/Understanding-Data-Compaction-in-3-Minutes.md
b/blog/Understanding-Data-Compaction-in-3-Minutes.md
index 488d95f716b..ae643b9aa07 100644
--- a/blog/Understanding-Data-Compaction-in-3-Minutes.md
+++ b/blog/Understanding-Data-Compaction-in-3-Minutes.md
@@ -128,4 +128,4 @@ Every data engineer has somehow been harassed by
complicated parameters and conf
## Conclusion
-This is how we keep our "storekeepers" working efficiently and
cost-effectively. If you wonder how these strategies and optimization work in
real practice, we tested Apache Doris with ClickBench. It reaches a
**compaction speed of 300,000 row/s**; in high-concurrency scenarios, it
maintains **a stable compaction score of around 50**. Also, we are planning to
implement auto-tuning and increase observability for the compaction mechanism.
If you are interested in the [Apache Doris](https:// [...]
+This is how we keep our "storekeepers" working efficiently and
cost-effectively. If you wonder how these strategies and optimization work in
real practice, we tested Apache Doris with ClickBench. It reaches a
**compaction speed of 300,000 row/s**; in high-concurrency scenarios, it
maintains **a stable compaction score of around 50**. Also, we are planning to
implement auto-tuning and increase observability for the compaction mechanism.
If you are interested in the [Apache Doris](https:// [...]
diff --git
a/blog/a-fast-secure-high-available-real-time-data-warehouse-based-on-apache-doris.md
b/blog/a-fast-secure-high-available-real-time-data-warehouse-based-on-apache-doris.md
index 5d79a81fb6c..010dc01f79d 100644
---
a/blog/a-fast-secure-high-available-real-time-data-warehouse-based-on-apache-doris.md
+++
b/blog/a-fast-secure-high-available-real-time-data-warehouse-based-on-apache-doris.md
@@ -147,4 +147,4 @@ Disaster recovery is crucial for the financial industry.
The user leverages the
## Conclusion
-We appreciate the user for their active
[communication](https://doris.apache.org/slack) with us along the way and are
glad to see so many Apache Doris features fit in their needs. They are also
planning on exploring federated query, compute-storage separation, and auto
maintenance with Apache Doris. We look forward to more best practice and
feedback from them.
+We appreciate the user for their active
[communication](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_a-fast-secure-high-available-real-time-data-warehouse-based)
with us along the way and are glad to see so many Apache Doris features fit in
their needs. They are also planning on exploring federated query,
compute-storage separation, and auto maintenance with Apache Doris. We look
forward to more best practice and feedback from them.
diff --git
a/blog/a-financial-anti-fraud-solution-based-on-the-apache-doris-data-warehouse.md
b/blog/a-financial-anti-fraud-solution-based-on-the-apache-doris-data-warehouse.md
index 0a0018a54f3..80cb80113c8 100644
---
a/blog/a-financial-anti-fraud-solution-based-on-the-apache-doris-data-warehouse.md
+++
b/blog/a-financial-anti-fraud-solution-based-on-the-apache-doris-data-warehouse.md
@@ -105,4 +105,4 @@ So far, the bank has added nearly 100 alerting rules for
various risk types to t
## Conclusion
-For a comprehensive anti-fraud solution, the bank conducts full-scale
real-time monitoring and reporting for all their data workflows. Then, for each
transaction, they look into the multiple dimensions of it to identify risks.
For the suspicious transactions reported by the bank customers, they perform
federated queries to retrieve the full details of them. Also, an auto alerting
mechanism is always on patrol to safeguard the whole system. These are the
various types of analytic workload [...]
+For a comprehensive anti-fraud solution, the bank conducts full-scale
real-time monitoring and reporting for all their data workflows. Then, for each
transaction, they look into the multiple dimensions of it to identify risks.
For the suspicious transactions reported by the bank customers, they perform
federated queries to retrieve the full details of them. Also, an auto alerting
mechanism is always on patrol to safeguard the whole system. These are the
various types of analytic workload [...]
diff --git
a/blog/ai-unicorn-minimax-from-loki-and-built-a-pb-scale-logging-system-with-doris.md
b/blog/ai-unicorn-minimax-from-loki-and-built-a-pb-scale-logging-system-with-doris.md
index 23572034c6e..bddd6c1f197 100644
---
a/blog/ai-unicorn-minimax-from-loki-and-built-a-pb-scale-logging-system-with-doris.md
+++
b/blog/ai-unicorn-minimax-from-loki-and-built-a-pb-scale-logging-system-with-doris.md
@@ -143,4 +143,4 @@ After a successful initial experience with Apache Doris,
MiniMax proceeds with t
- **Lakehousing**: expand the use of Apache Doris include big data processing
and analysis within MiniMax, laying the foundation for a data lakehouse.
-If you have any questions or require assistance regarding Apache Doris, join
the [community](https://doris.apache.org/slack).
+If you have any questions or require assistance regarding Apache Doris, join
the
[community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_ai-unicorn-minimax-from-loki-and-built-a-pb-scale-logging-s).
diff --git
a/blog/apache-doris-for-log-and-time-series-data-analysis-in-netease.md
b/blog/apache-doris-for-log-and-time-series-data-analysis-in-netease.md
index b5561ea266e..f7237c96884 100644
--- a/blog/apache-doris-for-log-and-time-series-data-analysis-in-netease.md
+++ b/blog/apache-doris-for-log-and-time-series-data-analysis-in-netease.md
@@ -161,7 +161,7 @@ streaming_load_json_max_mb=250
During peak times, the data platform is undertaking up to 1 million TPS and a
writing throughput of 1GB/s. This is demanding for the system. Meanwhile, at
peak time, a large number of concurrent write operations are loading data into
lots of tables, but each individual write operation only involves a small
amount of data. Thus, it takes a long time to accumulate a batch, which is
contradictory to the data freshness requirement from the query side.
-As a result, the data platform was bottlenecked by data backlogs in Apache
Kafka. NetEase adopts the [Stream
Load](https://doris.apache.org/docs/2.0/data-operate/import/stream-load-manual)
method to ingest data from Kafka to Doris. So the key was to accelerate Stream
Load. After talking to the [Apache Doris
developers](https://doris.apache.org/slack), NetEase adopted two optimizations
for their log and time series data analysis:
+As a result, the data platform was bottlenecked by data backlogs in Apache
Kafka. NetEase adopts the [Stream
Load](https://doris.apache.org/docs/2.0/data-operate/import/stream-load-manual)
method to ingest data from Kafka to Doris. So the key was to accelerate Stream
Load. After talking to the [Apache Doris
developers](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_apache-doris-for-log-and-time-series-data-analysis-in-netea),
NetEase adopted two opt [...]
- **Single replica data loading**: Load one data replica and pull data from it
to generate more replicas. This avoids the overhead of ranking and creating
indexes for multiple replicas.
@@ -234,4 +234,4 @@ If you want to enable `support_phrase` for existing tables
that have already bee
## Conclusion
-Apache Doris supports the log and time series data analytic workloads of
NetEase with higher query performance and less storage consumption. Beyond
these, Apache Doris has other capabilities such as data lake analysis since it
is designed as an all-in-one big data analytic platform. If you want a quick
evaluation of whether Doris is right for your use case, come talk to the Doris
makers on [Slack](https://doris.apache.org/slack).
+Apache Doris supports the log and time series data analytic workloads of
NetEase with higher query performance and less storage consumption. Beyond
these, Apache Doris has other capabilities such as data lake analysis since it
is designed as an all-in-one big data analytic platform. If you want a quick
evaluation of whether Doris is right for your use case, come talk to the Doris
makers on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_apach
[...]
diff --git
a/blog/apache-doris-speeds-up-data-reporting-tagging-and-data-lake-analytics.md
b/blog/apache-doris-speeds-up-data-reporting-tagging-and-data-lake-analytics.md
index 88a91b8a72e..5b1fffa22dd 100644
---
a/blog/apache-doris-speeds-up-data-reporting-tagging-and-data-lake-analytics.md
+++
b/blog/apache-doris-speeds-up-data-reporting-tagging-and-data-lake-analytics.md
@@ -90,6 +90,6 @@ For easier deployment, they have also optimized their Deploy
on Yarn process via
## Conclusion
-For data reporting and customer tagging, Apache Doris smoothens data ingestion
and merging steps, and delivers high query performance based on its own design
and functionality. For data lake analytics, the user improves resource
efficiency by elastic scaling of clusters using the Compute Node. Along their
journey with Apache Doris, they have also developed a data ingestion task
prioritizing mechanism and contributed it to the Doris project. A gesture to
facilitate their use case ends up [...]
+For data reporting and customer tagging, Apache Doris smoothens data ingestion
and merging steps, and delivers high query performance based on its own design
and functionality. For data lake analytics, the user improves resource
efficiency by elastic scaling of clusters using the Compute Node. Along their
journey with Apache Doris, they have also developed a data ingestion task
prioritizing mechanism and contributed it to the Doris project. A gesture to
facilitate their use case ends up [...]
Check Apache Doris [repo](https://github.com/apache/doris) on GitHub
\ No newline at end of file
diff --git
a/blog/apache-doris-summit-asia-2023-what-can-you-expect-from-apache-doris-as-a-data-warehouse.md
b/blog/apache-doris-summit-asia-2023-what-can-you-expect-from-apache-doris-as-a-data-warehouse.md
index 95ec2a96f8f..99184fec15b 100644
---
a/blog/apache-doris-summit-asia-2023-what-can-you-expect-from-apache-doris-as-a-data-warehouse.md
+++
b/blog/apache-doris-summit-asia-2023-what-can-you-expect-from-apache-doris-as-a-data-warehouse.md
@@ -174,4 +174,4 @@ From the self-developed pre-aggregation storage engine,
materialized views, and
- We want to keep inspiring the data world by presenting more use cases.
- We want to provide more and better choices for users by collaborating with
partners along the data pipeline and cloud service providers.
-By choosing Apache Doris, you choose to stay in the heartbeat of innovation.
The [Apache Doris community](https://doris.apache.org/slack) awaits newcomers.
+By choosing Apache Doris, you choose to stay in the heartbeat of innovation.
The [Apache Doris
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_apache-doris-summit-asia-2023-what-can-you-expect-from-apac)
awaits newcomers.
diff --git
a/blog/arrow-flight-sql-in-apache-doris-for-10x-faster-data-transfer.md
b/blog/arrow-flight-sql-in-apache-doris-for-10x-faster-data-transfer.md
index c3372a8e43d..dae598d03ba 100644
--- a/blog/arrow-flight-sql-in-apache-doris-for-10x-faster-data-transfer.md
+++ b/blog/arrow-flight-sql-in-apache-doris-for-10x-faster-data-transfer.md
@@ -461,4 +461,4 @@ For Spark users, apart from connecting to Flight SQL Server
using JDBC and JAVA,
A number of enterprise users of Doris has tried loading data from Doris to
Python, Spark, and Flink using Arrow Flight SQL and enjoyed much faster data
reading speed. In the future, we plan to include the support for Arrow Flight
SQL in data writing, too. By then, most systems built with mainstream
programming languages will be able to read and write data from/to Apache Doris
by an ADBC client. That's high-speed data interaction which opens up numerous
possibilities. On our to-do list, w [...]
-Download [Apache Doris 2.1](https://doris.apache.org/download/) and get a
taste of 100 times faster data transfer powered by Arrow Flight SQL. If you
need assistance, come find us in the [Apache Doris developer and user
community](https://doris.apache.org/slack).
+Download [Apache Doris 2.1](https://doris.apache.org/download/) and get a
taste of 100 times faster data transfer powered by Arrow Flight SQL. If you
need assistance, come find us in the [Apache Doris developer and user
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_arrow-flight-sql-in-apache-doris-for-10x-faster-data-transf).
diff --git a/blog/auto-increment-columns-in-databases.md
b/blog/auto-increment-columns-in-databases.md
index b0430c8f996..d6c4733d475 100644
--- a/blog/auto-increment-columns-in-databases.md
+++ b/blog/auto-increment-columns-in-databases.md
@@ -408,5 +408,5 @@ Attention is required regarding:
## Conclusion
-AUTO_INCREMENT brings higher stability and reliability for Doris in
large-scale data processing. If it sounds like something you need, download
[Apache Doris](https://doris.apache.org/download/) and try it out. For issues
you come across along the way, join us in the [Apache Doris developer and user
community](https://doris.apache.org/slack) and we are happy to help.
+AUTO_INCREMENT brings higher stability and reliability for Doris in
large-scale data processing. If it sounds like something you need, download
[Apache Doris](https://doris.apache.org/download/) and try it out. For issues
you come across along the way, join us in the [Apache Doris developer and user
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_auto-increment-columns-in-databases)
and we are happy to help.
diff --git a/blog/auto-partition-in-apache-doris.md
b/blog/auto-partition-in-apache-doris.md
index 6446c73dd6b..dcfad8673e9 100644
--- a/blog/auto-partition-in-apache-doris.md
+++ b/blog/auto-partition-in-apache-doris.md
@@ -479,4 +479,4 @@ Plans for Auto Partition by LIST:
- Allow merging multiple values into the same partition based on specific
rules.
-Join [Apache Doris open-source community](https://doris.apache.org/slack) for
more information and further guidance.
+Join [Apache Doris open-source
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_auto-partition-in-apache-doris)
for more information and further guidance.
diff --git a/blog/breaking-down-data-silos-with-an-apache-doris-based-cdp.md
b/blog/breaking-down-data-silos-with-an-apache-doris-based-cdp.md
index d990e4765e1..9f8deabb2c3 100644
--- a/blog/breaking-down-data-silos-with-an-apache-doris-based-cdp.md
+++ b/blog/breaking-down-data-silos-with-an-apache-doris-based-cdp.md
@@ -130,4 +130,4 @@ In Apache Doris, this is implemented by the BITMAP
functions: `BITMAP_CONTAINS`
## Conclusion
-From CDP 1.0 to CDP 2.0, the insurance company adopts Apache Doris, a unified
data warehouse, to replace Spark+Impala+HBase+NebulaGraph. That increases their
data processing efficiency by breaking down the data silos and streamlining
data processing pipelines. In CDP 3.0 to come, they want to group their
customer by combining real-time tags and offline tags for more diversified and
flexible analysis. The [Apache Doris community](https://doris.apache.org/slack)
and the [VeloDB](https://ww [...]
+From CDP 1.0 to CDP 2.0, the insurance company adopts Apache Doris, a unified
data warehouse, to replace Spark+Impala+HBase+NebulaGraph. That increases their
data processing efficiency by breaking down the data silos and streamlining
data processing pipelines. In CDP 3.0 to come, they want to group their
customer by combining real-time tags and offline tags for more diversified and
flexible analysis. The [Apache Doris
community](https://doris.apache.org/slack?utm_source=website&utm_mediu [...]
diff --git a/blog/cross-cluster-replication-for-read-write.md
b/blog/cross-cluster-replication-for-read-write.md
index cfac43ed6d5..e18628dc1de 100644
--- a/blog/cross-cluster-replication-for-read-write.md
+++ b/blog/cross-cluster-replication-for-read-write.md
@@ -112,6 +112,6 @@ In the grocery store brand's case, they need to synchronize
a few tables from th
Using CCR in Apache Doris, the grocery store brand separates reading and
writing workloads into different clusters and thus improves overall system
stability. This solution delivers a real-time data synchronization latency of
about 1 second. To further ensure normal functioning, it has a real-time
monitoring and alerting mechanism so any issue will be notified and attended to
instantly, and a contingency plan to guarantee uninterrupted query services. It
also supports partition-based dat [...]
-CCR is also applicable when you need to build multiple data centers or derive
a test dataset from your production environment. For further guidance on CCR,
join the [Apache Doris community](https://doris.apache.org/slack).
+CCR is also applicable when you need to build multiple data centers or derive
a test dataset from your production environment. For further guidance on CCR,
join the [Apache Doris
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_cross-cluster-replication-for-read-write).
\ No newline at end of file
diff --git
a/blog/data-analysis-for-live-streaming-what-happens-in-real-time-is-analyzed-in-real-time.md
b/blog/data-analysis-for-live-streaming-what-happens-in-real-time-is-analyzed-in-real-time.md
index 3afda6b56ee..21d389fd970 100644
---
a/blog/data-analysis-for-live-streaming-what-happens-in-real-time-is-analyzed-in-real-time.md
+++
b/blog/data-analysis-for-live-streaming-what-happens-in-real-time-is-analyzed-in-real-time.md
@@ -214,7 +214,7 @@ limit 1,10;
## Conclusion
-Data analysis in live streaming is challenging for the underlying database,
but it is also where the key competitiveness of Apache Doris comes to play.
First of all, Apache Doris can handle most data processing workloads, so
platform builders don't have to worry about putting many components together
and consequential maintenance issues. Secondly, it has a lot of
query-accelerating features, including but not limited to indexes. After
tackling the speed issues, the [Apache Doris develope [...]
+Data analysis in live streaming is challenging for the underlying database,
but it is also where the key competitiveness of Apache Doris comes to play.
First of all, Apache Doris can handle most data processing workloads, so
platform builders don't have to worry about putting many components together
and consequential maintenance issues. Secondly, it has a lot of
query-accelerating features, including but not limited to indexes. After
tackling the speed issues, the [Apache Doris develope [...]
diff --git
a/blog/empowering-cyber-security-by-enabling-seven-times-faster-log-analysis.md
b/blog/empowering-cyber-security-by-enabling-seven-times-faster-log-analysis.md
index ccbc5cddc80..0e1250a1e62 100644
---
a/blog/empowering-cyber-security-by-enabling-seven-times-faster-log-analysis.md
+++
b/blog/empowering-cyber-security-by-enabling-seven-times-faster-log-analysis.md
@@ -140,4 +140,4 @@ Apart from cluster management, Doris Manager provides a
visualized WebUI for log

-After a month-long trial run, they officially replaced their old LSAS with the
Apache Doris-based system for production, and achieved great results as they
expected. Now, they ingest their 100s of billions of new logs every day via the
[Routine
Load](https://doris.apache.org/docs/dev/data-operate/import/import-way/routine-load-manual/)
method at a speed 3 times as fast as before. Among the 7-time overall query
performance increase, they benefit from a speedup of over 20 times in full-tex
[...]
+After a month-long trial run, they officially replaced their old LSAS with the
Apache Doris-based system for production, and achieved great results as they
expected. Now, they ingest their 100s of billions of new logs every day via the
[Routine
Load](https://doris.apache.org/docs/dev/data-operate/import/import-way/routine-load-manual/)
method at a speed 3 times as fast as before. Among the 7-time overall query
performance increase, they benefit from a speedup of over 20 times in full-tex
[...]
diff --git a/blog/evolution-of-the-apache-doris-execution-engine.md
b/blog/evolution-of-the-apache-doris-execution-engine.md
index 45345f2b2b0..de0a632e196 100644
--- a/blog/evolution-of-the-apache-doris-execution-engine.md
+++ b/blog/evolution-of-the-apache-doris-execution-engine.md
@@ -230,4 +230,4 @@ From the Volcano Model to the Pipeline Execution Engine,
Apache Doris 2.0.0 has
What's next in our roadmap is to support spilling data to disk in PipelineX to
further improve query speed and system reliability. We also plan to advance
further in terms of automation, such as self-adaptive concurrency and auto
execution plan optimization, accompanied by NUMA technologies to harvest better
performance from hardware resources.
-If you want to talk to the amazing Doris developers who lead these changes,
you are more than welcome to join the [Apache
Doris](https://doris.apache.org/slack) community.
+If you want to talk to the amazing Doris developers who lead these changes,
you are more than welcome to join the [Apache
Doris](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_evolution-of-the-apache-doris-execution-engine)
community.
diff --git
a/blog/from-elasticsearch-to-apache-doris-upgrading-an-observability-platform.md
b/blog/from-elasticsearch-to-apache-doris-upgrading-an-observability-platform.md
index 7c739fbaf16..81dfc55cad6 100644
---
a/blog/from-elasticsearch-to-apache-doris-upgrading-an-observability-platform.md
+++
b/blog/from-elasticsearch-to-apache-doris-upgrading-an-observability-platform.md
@@ -161,8 +161,8 @@ Currently, the Variant type requires extra type assertion,
we plan to automate t
## Conclusion
-GuanceDB's transition from Elasticsearch to Apache Doris showcases a big
stride in improving data processing speed and reducing costs. For these
purposes, Apache Doris has optimized itself in the two major aspects of data
processing: data integration and data analysis. It has expanded its schemaless
support to flexibly accommodate more data types, introduced features like
inverted index and tiered storage to enable faster and more cost-effective
queries. Evolution is an ongoing process. [...]
+GuanceDB's transition from Elasticsearch to Apache Doris showcases a big
stride in improving data processing speed and reducing costs. For these
purposes, Apache Doris has optimized itself in the two major aspects of data
processing: data integration and data analysis. It has expanded its schemaless
support to flexibly accommodate more data types, introduced features like
inverted index and tiered storage to enable faster and more cost-effective
queries. Evolution is an ongoing process. [...]
Check Apache Doris GitHub [repo](https://github.com/apache/doris)
-Find Apache Doris makers on [Slack](https://doris.apache.org/slack)
+Find Apache Doris makers on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_from-elasticsearch-to-apache-doris-upgrading-an-observabili)
diff --git
a/blog/how-big-data-is-saving-lives-in-real-time-iov-data-analytics-helps-prevent-accidents.md
b/blog/how-big-data-is-saving-lives-in-real-time-iov-data-analytics-helps-prevent-accidents.md
index 068d6095cfe..4af1843f78e 100644
---
a/blog/how-big-data-is-saving-lives-in-real-time-iov-data-analytics-helps-prevent-accidents.md
+++
b/blog/how-big-data-is-saving-lives-in-real-time-iov-data-analytics-helps-prevent-accidents.md
@@ -110,5 +110,5 @@ Building a data platform to suit your use case is not easy,
I hope this post hel
Apache Doris [GitHub repo](https://github.com/apache/doris)
-Find Apache Doris makers on [Slack](https://doris.apache.org/slack)
+Find Apache Doris makers on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_how-big-data-is-saving-lives-in-real-time-iov-data-analytic)
diff --git
a/blog/introduction-to-apache-doris-a-next-generation-real-time-data-warehouse.md
b/blog/introduction-to-apache-doris-a-next-generation-real-time-data-warehouse.md
index a4526f2bef5..83e3da6937f 100644
---
a/blog/introduction-to-apache-doris-a-next-generation-real-time-data-warehouse.md
+++
b/blog/introduction-to-apache-doris-a-next-generation-real-time-data-warehouse.md
@@ -171,4 +171,4 @@ Roughly speaking, for a data asset consisting of 80% cold
data, tiered storage w
## The Apache Doris Community
-This is an overview of Apache Doris, an open-source real-time data warehouse.
It is actively evolving with an agile release schedule, and the
[community](https://doris.apache.org/slack) embraces any questions, ideas, and
feedback.
+This is an overview of Apache Doris, an open-source real-time data warehouse.
It is actively evolving with an agile release schedule, and the
[community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_introduction-to-apache-doris-a-next-generation-real-time-da)
embraces any questions, ideas, and feedback.
diff --git
a/blog/inverted-index-accelerates-text-searches-by-40-time-apache-doris.md
b/blog/inverted-index-accelerates-text-searches-by-40-time-apache-doris.md
index 626c6147a2d..d06ac62992f 100644
--- a/blog/inverted-index-accelerates-text-searches-by-40-time-apache-doris.md
+++ b/blog/inverted-index-accelerates-text-searches-by-40-time-apache-doris.md
@@ -508,4 +508,4 @@ Inverted index has been available in Apache Doris for
almost a year and stood th
- **Self-defined tokenization**: provides a user-defined tokenizer to fit in
different use cases.
- **More data types**: Users will be able to create inverted index for complex
data types including Array and Map.
-If you encounter any issues while trying it out in Apache Doris or would like
to know more details, join our [Slack](https://doris.apache.org/slack)
community and talk to us!
+If you encounter any issues while trying it out in Apache Doris or would like
to know more details, join our
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_inverted-index-accelerates-text-searches-by-40-time-apache)
community and talk to us!
diff --git a/blog/job-scheduler-for-task-automation.md
b/blog/job-scheduler-for-task-automation.md
index c21b4e98a6b..54f0dcd6782 100644
--- a/blog/job-scheduler-for-task-automation.md
+++ b/blog/job-scheduler-for-task-automation.md
@@ -202,7 +202,7 @@ In addition, for transactional tasks, the Job Scheduler can
ensure data consiste
The Doris Job Scheduler is a Swiss Army Knife. It is not only useful in ETL
and data lake analytics as we mentioned, but also critical for the
implementation of [asynchronous materialized
views](https://doris.apache.org/docs/query/view-materialized-view/async-materialized-view).
An asynchronous materialized view is a pre-computed result set. Unlike normal
materialized views, it can be built on multiple tables. Thus, as you can
imagine, changes in any of the source tables will lead to the [...]
-Where are we going with the Doris Job Scheduler? The [Apache Doris developer
community](https://doris.apache.org/slack) is looking at:
+Where are we going with the Doris Job Scheduler? The [Apache Doris developer
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_job-scheduler-for-task-automation)
is looking at:
- Displaying the distribution of tasks executed in different time slots on the
WebUI.
diff --git a/blog/log-analysis-elasticsearch-vs-apache-doris.md
b/blog/log-analysis-elasticsearch-vs-apache-doris.md
index a23660b5f9c..6f1d105c0b6 100644
--- a/blog/log-analysis-elasticsearch-vs-apache-doris.md
+++ b/blog/log-analysis-elasticsearch-vs-apache-doris.md
@@ -351,4 +351,4 @@ SELECT * FROM log_table WHERE request MATCH_ALL 'image faq'
ORDER BY ts DESC LIM
If you are looking for an efficient log analytic solution, Apache Doris is
friendly to anyone equipped with SQL knowledge; if you find friction with the
ELK stack, try Apache Doris provides better schema-free support, enables faster
data writing and queries, and brings much less storage burden.
-But we won't stop here. We are going to provide more features to facilitate
log analysis. We plan to add more complicated data types to inverted index, and
support BKD index to make Apache Doris a fit for geo data analysis. We also
plan to expand capabilities in semi-structured data analysis, such as working
on the complex data types (Array, Map, Struct, JSON) and high-performance
string matching algorithm. And we welcome any [user feedback and development
advice](https://doris.apache.or [...]
+But we won't stop here. We are going to provide more features to facilitate
log analysis. We plan to add more complicated data types to inverted index, and
support BKD index to make Apache Doris a fit for geo data analysis. We also
plan to expand capabilities in semi-structured data analysis, such as working
on the complex data types (Array, Map, Struct, JSON) and high-performance
string matching algorithm. And we welcome any [user feedback and development
advice](https://doris.apache.or [...]
diff --git a/blog/migrate-lakehouse-from-bigquery-to-doris.md
b/blog/migrate-lakehouse-from-bigquery-to-doris.md
index e66ca62735b..74b86ec5d06 100644
--- a/blog/migrate-lakehouse-from-bigquery-to-doris.md
+++ b/blog/migrate-lakehouse-from-bigquery-to-doris.md
@@ -32,7 +32,7 @@ under the License.
-->
:::tip Special Thanks
-The Apache Doris community would like to extend our gratitude to Dien for
sharing his valuable experience and best practices in migrating from BigQuery
to Apache Doris in this insightful and informative article. Dien is also an
active member of the Apache Doris open-source
[community](https://doris.apache.org/slack), and we are truly appreciative of
his ongoing support.
+The Apache Doris community would like to extend our gratitude to Dien for
sharing his valuable experience and best practices in migrating from BigQuery
to Apache Doris in this insightful and informative article. Dien is also an
active member of the Apache Doris open-source
[community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_migrate-lakehouse-from-bigquery-to-doris),
and we are truly appreciative of his ongoing support.
:::
This article is written by [Dien, Tran
Thanh](https://www.linkedin.com/in/dien-tran-thanh-19275b14a/) and originally
posted on
[Medium](https://dientt.medium.com/migrate-data-platform-từ-bigquery-sang-apache-doris-giúp-giảm-chi-phí-từ-6-000-xuống-còn-1-500-40ba9b22967e).
@@ -183,7 +183,7 @@ The implementation was carried out by 1 Data Engineer, 1
Software Engineer, and
- Data replication between nodes can sometimes lose synchronization due to
network issues or other reasons, and the automatic replication retry mechanism
may not be successful. In such cases, it is necessary to set up an external
worker mechanism for automatic handling (Doris manages a storage unit called
Tablet. The metadata on each node records the ETL data into a specific table,
and the metadata version gets updated. Doris provides a SQL-based method to
handle desynchronization).
-- New versions may occasionally have bugs, so it's recommended to check if the
[community](https://doris.apache.org/slack) has reported any issues before
deciding to upgrade.
+- New versions may occasionally have bugs, so it's recommended to check if the
[community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_migrate-lakehouse-from-bigquery-to-doris)
has reported any issues before deciding to upgrade.
- Limited support for processing Vietnamese text, with the full-text search
feature not performing well in Vietnamese.
diff --git a/blog/migrating-from-clickhouse-to-apache-doris-what-happened.md
b/blog/migrating-from-clickhouse-to-apache-doris-what-happened.md
index 85dd1b1218a..a3a6af32eb4 100644
--- a/blog/migrating-from-clickhouse-to-apache-doris-what-happened.md
+++ b/blog/migrating-from-clickhouse-to-apache-doris-what-happened.md
@@ -160,4 +160,4 @@ In terms of CPU and memory consumption, Apache Doris
maintained stable cluster l
## Future Directions
-As the migration goes on, the user works closely with the [Doris
community](https://doris.apache.org/slack), and their feedback has contributed
to the making of [Apache Doris
2.0.0](https://doris.apache.org/docs/dev/releasenotes/release-2.0.0/). We will
continue assisting them in their migration from Kylin and Druid to Doris, and
we look forward to see their Doris-based unified data platform come into being.
+As the migration goes on, the user works closely with the [Doris
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_migrating-from-clickhouse-to-apache-doris-what-happened),
and their feedback has contributed to the making of [Apache Doris
2.0.0](https://doris.apache.org/docs/dev/releasenotes/release-2.0.0/). We will
continue assisting them in their migration from Kylin and Druid to Doris, and
we look forward to see their Doris-based unified [...]
diff --git a/blog/multi-tenant-workload-isolation-in-apache-doris.md
b/blog/multi-tenant-workload-isolation-in-apache-doris.md
index 834b106d193..eb0c0412a8f 100644
--- a/blog/multi-tenant-workload-isolation-in-apache-doris.md
+++ b/blog/multi-tenant-workload-isolation-in-apache-doris.md
@@ -222,4 +222,4 @@ In future releases, we will keep improving user experience
of the Workload Group
- The main idea of Resource Tag is to group the BE nodes, while that of
Workload Group is to further divide the resources of a single BE node. For
users to grasp these ideas, they need to learn about the concept of BE nodes in
Doris first. However, from an operational perspective, users only need to
understand the resource consumption percentage of each of their workloads and
what priority they should have when cluster load is saturated. Thus, we will
try and figure out a way to flatten [...]
-For further assistance on workload isolation in Apache Doris, join the [Apache
Doris community](https://doris.apache.org/slack).
+For further assistance on workload isolation in Apache Doris, join the [Apache
Doris
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_multi-tenant-workload-isolation-in-apache-doris).
diff --git a/blog/release-note-2.0.0.md b/blog/release-note-2.0.0.md
index 28f7316306c..10e7dc8e3ee 100644
--- a/blog/release-note-2.0.0.md
+++ b/blog/release-note-2.0.0.md
@@ -235,5 +235,5 @@ This feature allows for higher availability of data,
read/write workload separat
To make Apache Doris 2.0.0 production-ready, we invited hundreds of enterprise
users to engage in the testing and optimized it for better performance,
stability, and usability. In the next phase, we will continue responding to
user needs with agile release planning. We plan to launch 2.0.1 in late August
and 2.0.2 in September, as we keep fixing bugs and adding new features. We also
plan to release an early version of 2.1 in September to bring a few
long-requested capabilities to you. Fo [...]
-If you have any questions or ideas when investigating, testing, and deploying
Apache Doris, please find us on [Slack](https://doris.apache.org/slack). Our
developers will be happy to hear them and provide targeted support.
+If you have any questions or ideas when investigating, testing, and deploying
Apache Doris, please find us on
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_release-note-2.0.0).
Our developers will be happy to hear them and provide targeted support.
diff --git a/blog/release-note-2.1.0.md b/blog/release-note-2.1.0.md
index dd33ab958f7..20c8d5fa2c6 100644
--- a/blog/release-note-2.1.0.md
+++ b/blog/release-note-2.1.0.md
@@ -40,7 +40,7 @@ Dear Apache Doris community, we are thrilled to announce the
advent of Apache Do
- **Better workload management**: optimizations of the Workload Group
mechanism for higher performance stability and the display of SQL resource
consumption in the runtime.
-We appreciate the 237 contributors who made nearly 6000 commits in total to
the Apache Doris project, and the nearly 100 enterprise users who provided
valuable feedback. We will keep aiming for the stars with our agile release
planning, and we appreciate your feedback in the [Apache Doris developer and
user community](https://doris.apache.org/slack).
+We appreciate the 237 contributors who made nearly 6000 commits in total to
the Apache Doris project, and the nearly 100 enterprise users who provided
valuable feedback. We will keep aiming for the stars with our agile release
planning, and we appreciate your feedback in the [Apache Doris developer and
user
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_release-note-2.1.0).
**Download from GitHub**: https://github.com/apache/doris/releases
diff --git a/blog/release-note-3.0.0.md b/blog/release-note-3.0.0.md
index 1a238f88e16..025f91895bd 100644
--- a/blog/release-note-3.0.0.md
+++ b/blog/release-note-3.0.0.md
@@ -463,7 +463,7 @@ Before the official release of version 3.0, the
compute-storage decoupled mode o
We highly recommend users with compute-storage decoupling needs to download
version 3.0 and experience it firsthand.
-Going forward, we will accelerate our release iteration cycle to deliver a
more stable version experience for all users. Feel free to join us in the
[Apache Doris community](https://doris.apache.org/slack) and engage directly
with the core developers.
+Going forward, we will accelerate our release iteration cycle to deliver a
more stable version experience for all users. Feel free to join us in the
[Apache Doris
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_release-note-3.0.0)
and engage directly with the core developers.
## Credits
diff --git a/blog/tencent-music-migrate-elasticsearch-to-doris.md
b/blog/tencent-music-migrate-elasticsearch-to-doris.md
index 75e29833875..cc012ede67a 100644
--- a/blog/tencent-music-migrate-elasticsearch-to-doris.md
+++ b/blog/tencent-music-migrate-elasticsearch-to-doris.md
@@ -186,4 +186,4 @@ The migration from Elasticsearch to Apache Doris has
yielded impressive gains. W
By replacing its Elasticsearch cluster with Doris, TME has unified its content
library's search and analytics engines into a single, streamlined platform. The
system now supports complex custom tag-based segmentation with sub-second
response. The next-phase plan of TME is to explore broader use cases of Apache
Doris and prepare to adopt the [compute-storage decoupled
mode](https://doris.apache.org/docs/3.0/compute-storage-decoupled/overview) to
drive even greater cost efficiency.
-For direct communication, real-world insights, and best practices, join
[#elasticsearch-to-doris](https://apachedoriscommunity.slack.com/archives/C08CQKX20R5)
channel in the [Apache Doris community](https://doris.apache.org/slack).
+For direct communication, real-world insights, and best practices, join
[#elasticsearch-to-doris](https://apachedoriscommunity.slack.com/archives/C08CQKX20R5)
channel in the [Apache Doris
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_tencent-music-migrate-elasticsearch-to-doris).
diff --git a/blog/variant-in-apache-doris-2.1.md
b/blog/variant-in-apache-doris-2.1.md
index 55854c79284..774c9bb44f4 100644
--- a/blog/variant-in-apache-doris-2.1.md
+++ b/blog/variant-in-apache-doris-2.1.md
@@ -428,4 +428,4 @@ The Doris-based solution also delivers lower CPU usage in
data writing and highe
The Variant data type has stood the test of many users before the official
release of Apache Doris 2.1.0. It is production-available now. In the future,
we plan to realize more lightweight changes for Variant to facilitate data
modeling.
-For more information about Variant and guides on how to build a
semi-structured data analytics solution for your case, come talk to the [Apache
Doris developer team](https://doris.apache.org/slack).
+For more information about Variant and guides on how to build a
semi-structured data analytics solution for your case, come talk to the [Apache
Doris developer
team](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_variant-in-apache-doris-2.1).
diff --git
a/blog/why-apache-doris-is-best-alternatives-for-real-time-analytics.md
b/blog/why-apache-doris-is-best-alternatives-for-real-time-analytics.md
index 57b6e4c9700..ba144b65fac 100644
--- a/blog/why-apache-doris-is-best-alternatives-for-real-time-analytics.md
+++ b/blog/why-apache-doris-is-best-alternatives-for-real-time-analytics.md
@@ -397,7 +397,7 @@ By 2023, the value of Doris with inverted indexes became
increasingly evident, l
The growth momentum has continued, and as of 2024, we are experiencing rapid
expansion, with over 100 companies now leveraging Doris to replace
Elasticsearch.
-Looking ahead, I am very much looking forward to what 2025 will bring. This
progress, advancing from the ground up to such significant milestones, has been
made possible by the incredible support from the Doris community users and
developers. We encourage everyone to join the [Apache Doris Slack
community](https://doris.apache.org/slack) and join the dedicated channel
[#elasticsearch-to-doris](https://apachedoriscommunity.slack.com/archives/C08CQKX20R5),
where you can receive technical a [...]
+Looking ahead, I am very much looking forward to what 2025 will bring. This
progress, advancing from the ground up to such significant milestones, has been
made possible by the incredible support from the Doris community users and
developers. We encourage everyone to join the [Apache Doris Slack
community](https://doris.apache.org/slack?utm_source=website&utm_medium=content&utm_content=blog_why-apache-doris-is-best-alternatives-for-real-time-analyti)
and join the dedicated channel [#elas [...]
More on Apache Doris:
diff --git a/community/how-to-contribute/contribute-to-doris.mdx
b/community/how-to-contribute/contribute-to-doris.mdx
index 7fc760b8619..85e005e21d8 100644
--- a/community/how-to-contribute/contribute-to-doris.mdx
+++ b/community/how-to-contribute/contribute-to-doris.mdx
@@ -185,7 +185,7 @@ Apache Doris welcomes developers, users, technical writers,
speakers, and commun
The sections below explain how to get started on each path.
:::tip Tip
-This guide may change as community processes evolve. Join the [Apache Doris
Slack community](https://doris.apache.org/slack) for the latest updates.
+This guide may change as community processes evolve. Join the [Apache Doris
Slack
community](https://doris.apache.org/slack?utm_source=website&utm_medium=docs&utm_content=contribute_guide)
for the latest updates.
:::
<Tabs groupId="contribution-path" queryString="path">
diff --git a/community/join-community.md b/community/join-community.md
index c5224b223ee..8423b3d0fae 100644
--- a/community/join-community.md
+++ b/community/join-community.md
@@ -50,7 +50,7 @@ The purpose and use case of each channel is as follows:
| Channel | Purpose | Entry Point |
|------|------|------|
-| Slack | Real-time discussion, questions, and answers (recommended for new
users) | <https://doris.apache.org/slack> |
+| Slack | Real-time discussion, questions, and answers (recommended for new
users) |
[https://doris.apache.org/slack](https://doris.apache.org/slack?utm_source=website&utm_medium=docs&utm_content=community_join_page)
|
| GitHub | Source code, issues, PRs, release notes |
<https://github.com/apache/doris> |
| Mailing list | Apache official asynchronous discussion and decision-making
channel | See [Subscribe to the mailing list](subscribe-mail-list) |
| Twitter | Global user cases, release updates, event announcements |
<https://twitter.com/doris_apache> |
@@ -62,7 +62,7 @@ The purpose and use case of each channel is as follows:
<!-- Knowledge type: Scenario routing -->
-- **To ask quick questions or join daily discussions**: join
[Slack](https://doris.apache.org/slack).
+- **To ask quick questions or join daily discussions**: join
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=docs&utm_content=community_join_page).
- **To report bugs or submit code**: visit
[GitHub](https://github.com/apache/doris), and refer to [Feedback](feedback)
and the [Community Contribution
Guide](how-to-contribute/contribute-to-doris.mdx).
- **To follow community decisions and release announcements**: [subscribe to
the mailing list](subscribe-mail-list).
- **To see user cases and technical insights**: follow
[Twitter](https://twitter.com/doris_apache) and
[LinkedIn](https://www.linkedin.com/company/doris-apache/).
diff --git a/developer_docs/slack-utm-convention.md
b/developer_docs/slack-utm-convention.md
new file mode 100644
index 00000000000..41acd759d72
--- /dev/null
+++ b/developer_docs/slack-utm-convention.md
@@ -0,0 +1,105 @@
+# Slack Link Attribution (UTM Convention)
+
+`https://doris.apache.org/slack` is the only Slack link we publish. It is a
+small tracking page (`src/pages/slack.tsx`) that records the click in the ASF
+Matomo instance and then forwards the visitor to the real invite link kept in
+`docusaurus.config.js` under `customFields.slackInviteUrl`. When the invite
+link is rotated, change it there and nothing else.
+
+Every channel keeps linking to `/slack`; what differs per channel is the UTM
+query string. Use these four standard parameters and nothing else:
+
+| Parameter | Question it answers | Allowed values |
+|---|---|---|
+| `utm_source` | Where does the visitor come from? | `website`, `github`,
`linkedin`, `reddit`, `youtube`, `medium`, `newsletter`, `webinar`,
`conference`, `partner`, or a specific organiser such as `awsug` |
+| `utm_medium` | What kind of traffic is it? | `website`, `docs`, `social`,
`community`, `content`, `event`, `offline`, `email` |
+| `utm_campaign` | Which campaign? (optional for permanent entry points) |
e.g. `doris_summit_2026`, `iceberg_webinar_202609`, `release_4_1` |
+| `utm_content` | Which exact CTA / placement? | e.g. `homepage_hero`,
`footer`, `docs_toc`, `github_readme`, `post_01`, `closing_slide`, `qr_code` |
+
+Value rules (the page enforces them, but write links this way so reports stay
+clean):
+
+- lowercase ASCII letters, digits, `.`, `_`, `-`; anything else becomes `_`
+- at most 64 characters per value
+- never use `|`, it is the field separator in the Matomo event name
+
+## Examples
+
+```text
+Website header
https://doris.apache.org/slack?utm_source=website&utm_medium=website&utm_content=homepage_hero
+Website footer
https://doris.apache.org/slack?utm_source=website&utm_medium=website&utm_content=footer
+Docs TOC
https://doris.apache.org/slack?utm_source=website&utm_medium=docs&utm_content=docs_toc
+GitHub README
https://doris.apache.org/slack?utm_source=github&utm_medium=community&utm_content=github_readme
+LinkedIn post
https://doris.apache.org/slack?utm_source=linkedin&utm_medium=social&utm_campaign=community_growth_h2_2026&utm_content=post_01
+Webinar
https://doris.apache.org/slack?utm_source=webinar&utm_medium=event&utm_campaign=iceberg_webinar_202609
+Conference QR code
https://doris.apache.org/slack?utm_source=awsug&utm_medium=offline&utm_campaign=awsug_uzbekistan_2026&utm_content=closing_slide
+```
+
+Links rendered by the website itself are generated with
+`buildSlackEntryUrl()` from
`src/components/slack-redirect/slack-attribution.logic.js`;
+do not hand-write them in React code.
+
+Markdown content on the site uses these fixed values:
+
+| Where | `utm_medium` | `utm_content` |
+|---|---|---|
+| Blog posts (`blog/`) | `content` | `blog_<file-name-in-lowercase>`, cut to
64 characters |
+| Community docs (`community/`) | `docs` | `community_join_page`,
`contribute_guide` |
+| Release notes (`releasenotes/`) | `docs` | `release_notes_<version>`, e.g.
`release_notes_3_0_0` |
+
+Never link to `join.slack.com` directly from content: the invite link
+rotates, and a direct link skips the tracking page entirely.
+
+## What the page records
+
+One Matomo event per click:
+
+```text
+Category: Community Acquisition
+Action: Slack Click
+Name: source|medium|campaign|content
+```
+
+Attribution priority is `utm_source` > `document.referrer` > direct:
+
+| Situation | Event name |
+|---|---|
+| UTM present | `github\|community\|\|github_readme` |
+| No `utm_source`, referrer available | `reddit.com\|referral\|\|` (host
without `www.`) |
+| Neither | `direct\|none\|\|` |
+
+Empty fields stay empty (`github|||` means the link only carried
+`utm_source=github`). Matomo also stores the full page URL and the referrer
+with the automatic page view, so the Referrers report can be used to discover
+sites that link to `/slack` without any UTM.
+
+Note that links rendered inside doris.apache.org use `rel="noreferrer"`, so a
+click from our own pages without UTM shows up as `direct`. That is why every
+in-site entry point carries UTM parameters.
+
+## Reading the numbers
+
+In Matomo (`https://analytics.apache.org/`, site id 43):
+
+1. Behaviour → Events → Event Names, category `Community Acquisition`.
+2. Split the name on `|` to aggregate by source, medium, campaign or content.
+3. Compare daily unique clicks with new Slack members for a rough
+ click-to-join rate.
+
+Clicks are not joins: once the visitor lands on `join.slack.com` we cannot see
+whether they completed the sign-up, so only aggregate trends are meaningful.
+
+## Timing
+
+The page queues the event in `_paq` immediately and redirects from the
+event's callback, i.e. as soon as matomo.js has handed the request to
+`navigator.sendBeacon`. matomo.js sends its automatic page view first and
+holds later requests for 800ms, so with the tracker present the redirect
+happens roughly 0.9s after the page has rendered.
+
+While matomo.js is still downloading the page waits up to 1.5s for it; if the
+script fetch already failed (ad blocker, 404) or no tracker is installed
+(development builds) it redirects at once - Resource Timing is used to tell
+the two apart. A hard cap of 2.5s applies in every case. Browsers without
+JavaScript are forwarded by a `<noscript>` meta refresh. The constants live at
+the top of `src/components/slack-redirect/SlackRedirect.tsx`.
diff --git a/docusaurus.config.js b/docusaurus.config.js
index f8361cbda32..18e2b67523e 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -291,6 +291,12 @@ const config = {
],
projectName: 'apache/doris-website', // Usually your repo name.
customFields: {
+ // Real Slack invite link. `/slack` (src/pages/slack.tsx) records the
+ // click in Matomo and then forwards visitors here, so every public
+ // channel keeps linking to https://doris.apache.org/slack and this is
+ // the only value to update when the invite is rotated.
+ slackInviteUrl:
+
'https://join.slack.com/t/apachedoriscommunity/shared_invite/zt-3wvgezmm8-lh5XRaLg0~9AF44ojdIBfw',
// The public HTTPS reverse proxy is the default browser entry point.
// Local development can still replace this build-time value when
needed.
profileAnalysisApiBaseUrl: process.env.PROFILE_ANALYSIS_API_BASE_URL
?? 'https://agent.velodb.io',
@@ -362,10 +368,10 @@ const config = {
// pointing at an in-site path must go through
// createRedirects below, otherwise plugin-client-redirects
// rejects cross-locale entries during single-locale
builds.
- {
- from: '/slack',
- to:
'https://join.slack.com/t/apachedoriscommunity/shared_invite/zt-3wvgezmm8-lh5XRaLg0~9AF44ojdIBfw'
- },
+ //
+ // `/slack` used to be listed here. It is now a real page
+ // (src/pages/slack.tsx) that records the click in Matomo
+ // before forwarding to customFields.slackInviteUrl.
],
createRedirects(existingPath) {
const redirects = [];
@@ -477,7 +483,10 @@ const config = {
const items = await defaultCreateSitemapItems(rest);
const filteredItems = items.filter(item => {
const pathname = new
URL(item.url).pathname.replace(/\/+$/, '');
- if (['/search',
'/zh-CN/search'].includes(pathname)) return false;
+ // /slack is a tracking redirect, not content.
+ if (['/search', '/zh-CN/search', '/slack',
'/zh-CN/slack'].includes(pathname)) {
+ return false;
+ }
return true;
});
for (let item of filteredItems) {
diff --git
a/i18n/zh-CN/docusaurus-plugin-content-docs-community/current/how-to-contribute/contribute-to-doris.mdx
b/i18n/zh-CN/docusaurus-plugin-content-docs-community/current/how-to-contribute/contribute-to-doris.mdx
index a2b7d0b0d64..74dcddb1b61 100644
---
a/i18n/zh-CN/docusaurus-plugin-content-docs-community/current/how-to-contribute/contribute-to-doris.mdx
+++
b/i18n/zh-CN/docusaurus-plugin-content-docs-community/current/how-to-contribute/contribute-to-doris.mdx
@@ -185,7 +185,7 @@ Apache Doris 欢迎全球开发者、用户、技术作者、讲师和社区组
根据不同的参与路径,社区提供了对应的参与指南。
:::tip 提示
-指南内容可能会随社区流程调整而变化。欢迎加入 [Apache Doris Slack 社群](https://doris.apache.org/slack)
获取实时动态。
+指南内容可能会随社区流程调整而变化。欢迎加入 [Apache Doris Slack
社群](https://doris.apache.org/slack?utm_source=website&utm_medium=docs&utm_content=contribute_guide)
获取实时动态。
:::
<Tabs groupId="contribution-path" queryString="path">
diff --git
a/i18n/zh-CN/docusaurus-plugin-content-docs-community/current/join-community.md
b/i18n/zh-CN/docusaurus-plugin-content-docs-community/current/join-community.md
index 85020be0ac3..e5aa7327b49 100644
---
a/i18n/zh-CN/docusaurus-plugin-content-docs-community/current/join-community.md
+++
b/i18n/zh-CN/docusaurus-plugin-content-docs-community/current/join-community.md
@@ -50,7 +50,7 @@ under the License.
| 渠道 | 定位 | 入口 |
|------|------|------|
-| Slack | 实时讨论、提问与答疑(推荐新用户首选) | <https://doris.apache.org/slack> |
+| Slack | 实时讨论、提问与答疑(推荐新用户首选) |
[https://doris.apache.org/slack](https://doris.apache.org/slack?utm_source=website&utm_medium=docs&utm_content=community_join_page)
|
| GitHub | 源码、Issue、PR、Release Note | <https://github.com/apache/doris> |
| 邮件列表 | Apache 官方异步讨论与决策渠道 | 参见 [订阅邮件列表](subscribe-mail-list) |
| Twitter | 全球用户案例、版本动态、活动预告 | <https://twitter.com/doris_apache> |
@@ -62,7 +62,7 @@ under the License.
<!-- 知识类型: 场景分流 -->
-- **想快速提问或参与日常讨论**:加入 [Slack](https://doris.apache.org/slack)。
+- **想快速提问或参与日常讨论**:加入
[Slack](https://doris.apache.org/slack?utm_source=website&utm_medium=docs&utm_content=community_join_page)。
- **想报告 Bug 或提交代码**:访问 [GitHub](https://github.com/apache/doris),参考
[问题反馈](feedback) 与 [社区参与指南](how-to-contribute/contribute-to-doris.mdx)。
- **想跟进社区决策与版本发布**:[订阅邮件列表](subscribe-mail-list)。
- **想看用户案例与技术见解**:关注 [Twitter](https://twitter.com/doris_apache) 与
[LinkedIn](https://www.linkedin.com/company/doris-apache/)。
diff --git a/releasenotes/v3.0/release-3.0.0.md
b/releasenotes/v3.0/release-3.0.0.md
index 36a20fe1dc2..f1a904529ff 100644
--- a/releasenotes/v3.0/release-3.0.0.md
+++ b/releasenotes/v3.0/release-3.0.0.md
@@ -441,7 +441,7 @@ Before the official release of version 3.0, the
compute-storage decoupled mode o
We highly recommend users with compute-storage decoupling needs to download
version 3.0 and experience it firsthand.
-Going forward, we will accelerate our release iteration cycle to deliver a
more stable version experience for all users. Feel free to join us in the
[Apache Doris community](https://doris.apache.org/slack) and engage directly
with the core developers.
+Going forward, we will accelerate our release iteration cycle to deliver a
more stable version experience for all users. Feel free to join us in the
[Apache Doris
community](https://doris.apache.org/slack?utm_source=website&utm_medium=docs&utm_content=release_notes_3_0_0)
and engage directly with the core developers.
## Credits
diff --git a/src/components/home-next/SlackCommunityNudge.tsx
b/src/components/home-next/SlackCommunityNudge.tsx
index 22a9102aa5a..d249f02658f 100644
--- a/src/components/home-next/SlackCommunityNudge.tsx
+++ b/src/components/home-next/SlackCommunityNudge.tsx
@@ -2,6 +2,7 @@ import React, { JSX, useCallback, useEffect, useRef, useState }
from 'react';
import { useLocation } from '@docusaurus/router';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import { normalizePathname } from '@site/src/utils/locale';
+import { buildSlackEntryUrl } from
'@site/src/components/slack-redirect/slack-attribution.logic';
import {
computeMascotPupilOffset,
getSlackNudgeBenefits,
@@ -10,7 +11,6 @@ import {
} from './SlackCommunityNudge.logic';
import './SlackCommunityNudge.scss';
-const SLACK_URL = 'https://doris.apache.org/slack';
const DORIS_SUMMIT_URL = 'https://apache-doris-summit.org/';
const DOCS_FEEDBACK_URL = 'https://github.com/apache/doris-website/issues/364';
const STORAGE_KEY = 'doris-home-slack-nudge-dismissed';
@@ -68,6 +68,10 @@ export function SlackCommunityNudge(): JSX.Element {
const lastPointerRef = useRef<{ x: number; y: number } | null>(null);
const normalizedPathname = normalizePathname(pathname, locales);
const showDocsFeedback = isDocumentationFeedbackPath(normalizedPathname);
+ const slackUrl = buildSlackEntryUrl({
+ medium: showDocsFeedback ? 'docs' : 'website',
+ content: 'mascot_nudge',
+ });
const openAutomatically = useCallback((ecosystemVisible: boolean) => {
const mountedAt = mountedAtRef.current ?? Date.now();
@@ -254,7 +258,7 @@ export function SlackCommunityNudge(): JSX.Element {
</ul>
<a
className="slack-community-nudge__cta"
- href={SLACK_URL}
+ href={slackUrl}
target="_blank"
rel="noopener noreferrer"
onClick={handleJoin}
diff --git a/src/components/home-next/sections/CommunitySection.tsx
b/src/components/home-next/sections/CommunitySection.tsx
index d9518b39d85..7fbc9c0a695 100644
--- a/src/components/home-next/sections/CommunitySection.tsx
+++ b/src/components/home-next/sections/CommunitySection.tsx
@@ -1,4 +1,5 @@
import React, { JSX } from 'react';
+import { buildSlackEntryUrl } from
'@site/src/components/slack-redirect/slack-attribution.logic';
import './CommunitySection.scss';
interface Channel {
@@ -19,7 +20,7 @@ const CHANNELS: Channel[] = [
},
{
name: 'Slack',
- href: 'https://doris.apache.org/slack',
+ href: buildSlackEntryUrl({ medium: 'website', content:
'homepage_community' }),
icon: (
<svg width="28" height="28" viewBox="0 0 24 24"
fill="currentColor" aria-hidden="true">
<path d="M5.042 15.165a2.528 2.528 0 01-2.52 2.523A2.528 2.528
0 010 15.165a2.527 2.527 0 012.522-2.52h2.52v2.52zm1.271 0a2.527 2.527 0
012.521-2.52 2.527 2.527 0 012.521 2.52v6.313A2.528 2.528 0 018.834 24a2.528
2.528 0 01-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 01-2.521-2.52A2.528
2.528 0 018.834 0a2.528 2.528 0 012.521 2.522v2.52H8.834zm0 1.271a2.528 2.528 0
012.521 2.521 2.528 2.528 0 01-2.521 2.521H2.522A2.528 2.528 0 010 8.834a2.528
2.528 0 012.522-2.521h6.312 [...]
diff --git a/src/components/home-next/sections/HeroSection.tsx
b/src/components/home-next/sections/HeroSection.tsx
index 4d80729128b..c697152a7ff 100644
--- a/src/components/home-next/sections/HeroSection.tsx
+++ b/src/components/home-next/sections/HeroSection.tsx
@@ -1,5 +1,6 @@
import React, { JSX, useState, useEffect, useMemo, useRef } from 'react';
import { StarGreenIcon } from '@site/src/components/Icons/star-green-icon';
+import { buildSlackEntryUrl } from
'@site/src/components/slack-redirect/slack-attribution.logic';
import './HeroSection.scss';
// ─── SVG atoms
───────────────────────────────────────────────────────────────
@@ -909,7 +910,7 @@ export function HeroSection(): JSX.Element {
</button>
<a
className="hero-next__btn
hero-next__btn--ghost"
- href="https://doris.apache.org/slack"
+ href={buildSlackEntryUrl({ medium: 'website',
content: 'homepage_hero' })}
target="_blank"
rel="noopener noreferrer"
>
diff --git a/src/components/slack-redirect/SlackRedirect.scss
b/src/components/slack-redirect/SlackRedirect.scss
new file mode 100644
index 00000000000..2b83fe1d4ff
--- /dev/null
+++ b/src/components/slack-redirect/SlackRedirect.scss
@@ -0,0 +1,109 @@
+@use '../shared/typography' as type;
+
+.slack-redirect {
+ --sr-green: var(--brand-primary-dark);
+ --sr-green-deep: var(--brand-primary-darker);
+ --sr-green-glow: var(--brand-primary-glow);
+ --sr-ink: var(--brand-ink);
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 100vh;
+ padding: 24px;
+ background:
+ radial-gradient(circle at 20% 0%, rgb(var(--brand-primary-glow-rgb) /
18%), transparent 42%),
+ linear-gradient(180deg, var(--brand-paper) 0%,
var(--brand-cream-light) 100%);
+ color: var(--sr-ink);
+ font-family: var(--font-family-base);
+
+ &__card {
+ width: min(420px, 100%);
+ padding: 28px 28px 24px;
+ border: 1px solid rgb(var(--brand-primary-rgb) / 22%);
+ border-radius: 8px;
+ background: var(--brand-paper);
+ box-shadow:
+ 0 28px 70px -34px rgb(var(--brand-primary-darker-rgb) / 0.4),
+ 0 14px 34px -24px rgb(var(--brand-ink-rgb) / 0.3);
+ text-align: center;
+ }
+
+ &__icon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 56px;
+ height: 56px;
+ margin-bottom: 16px;
+ border-radius: 50%;
+ background: rgb(var(--brand-primary-rgb) / 10%);
+ color: var(--sr-green);
+ }
+
+ &__eyebrow {
+ @include type.eyebrow;
+
+ margin: 0 0 8px;
+ color: var(--sr-green);
+ }
+
+ &__title {
+ @include type.mono-text(26px, 800, 1.15, -0.02em);
+
+ margin: 0 0 12px;
+ color: var(--sr-ink);
+ }
+
+ &__copy {
+ @include type.sans-text(type.$body, 400, 1.6);
+
+ margin: 0 0 20px;
+ color: rgb(var(--brand-ink-rgb) / 0.78);
+ }
+
+ &__link {
+ color: var(--sr-green);
+ font-weight: 600;
+ text-decoration: underline;
+ text-underline-offset: 3px;
+
+ &:hover,
+ &:focus-visible {
+ color: var(--sr-green-deep);
+ }
+ }
+
+ &__progress {
+ height: 4px;
+ overflow: hidden;
+ border-radius: 999px;
+ background: rgb(var(--brand-primary-rgb) / 14%);
+ }
+
+ &__progress-bar {
+ display: block;
+ width: 40%;
+ height: 100%;
+ border-radius: inherit;
+ background: linear-gradient(90deg, var(--sr-green),
var(--sr-green-glow));
+ animation: slack-redirect-slide 1.2s ease-in-out infinite;
+ }
+}
+
+@keyframes slack-redirect-slide {
+ from {
+ transform: translateX(-100%);
+ }
+
+ to {
+ transform: translateX(260%);
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .slack-redirect__progress-bar {
+ width: 100%;
+ animation: none;
+ }
+}
diff --git a/src/components/slack-redirect/SlackRedirect.tsx
b/src/components/slack-redirect/SlackRedirect.tsx
new file mode 100644
index 00000000000..5bd06d2d239
--- /dev/null
+++ b/src/components/slack-redirect/SlackRedirect.tsx
@@ -0,0 +1,164 @@
+import React, { JSX, useEffect } from 'react';
+import Head from '@docusaurus/Head';
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import { SlackIcon } from '@site/src/components/Icons/slack';
+import {
+ SLACK_EVENT_ACTION,
+ SLACK_EVENT_CATEGORY,
+ buildSlackEventName,
+ getMatomoScriptUrl,
+ resolveSlackAttribution,
+ resolveTrackerState,
+} from './slack-attribution.logic';
+import type { MatomoThemeConfig } from './slack-attribution.logic';
+import './SlackRedirect.scss';
+
+// How the wait for Matomo works (see resolveTrackerState for the states):
+//
+// - The click event is queued in `_paq` right away with a callback that
+// performs the redirect, so as soon as matomo.js has handed the request to
+// navigator.sendBeacon we leave. matomo.js sends the automatic pageview
+// first and holds every later request for 800ms (+50ms per queued request)
+// to avoid creating a brand-new visitor twice, so with the tracker present
+// the redirect happens ~0.9s after this effect runs.
+// - While matomo.js is still downloading we keep waiting, but no longer than
+// TRACKER_WAIT_MS. REDIRECT_MAX_WAIT_MS is the hard cap that also covers
+// a tracker that loaded late (TRACKER_WAIT_MS + the 800ms gap).
+// - If the script fetch already finished without producing a tracker (ad
+// blocker, 404, network error) or no tracker is installed at all
+// (development builds), there is nothing to wait for and we leave at once.
+// Resource Timing tells the two apart from "still downloading".
+const TRACKER_POLL_MS = 100;
+const TRACKER_WAIT_MS = 1500;
+const REDIRECT_MAX_WAIT_MS = 2500;
+
+type MatomoWindow = Window & {
+ _paq?: unknown[][];
+ Matomo?: unknown;
+};
+
+function hasFinishedFetching(url: string): boolean {
+ if (typeof performance === 'undefined' || typeof
performance.getEntriesByName !== 'function') {
+ return false;
+ }
+ return performance.getEntriesByName(url).length > 0;
+}
+
+function hasScriptElement(url: string): boolean {
+ return Array.from(document.scripts).some(script => script.src === url);
+}
+
+function readSlackInviteUrl(customFields: Record<string, unknown> |
undefined): string {
+ const value = customFields?.slackInviteUrl;
+ if (typeof value !== 'string' || value === '') {
+ throw new Error(
+ 'customFields.slackInviteUrl is missing from docusaurus.config.js;
/slack cannot forward visitors without it.',
+ );
+ }
+ return value;
+}
+
+export default function SlackRedirect(): JSX.Element {
+ const { siteConfig } = useDocusaurusContext();
+ const slackInviteUrl = readSlackInviteUrl(siteConfig.customFields);
+ const trackerScriptUrl = getMatomoScriptUrl(siteConfig.themeConfig.matomo
as MatomoThemeConfig | undefined);
+
+ useEffect(() => {
+ const matomoWindow = window as MatomoWindow;
+ const attribution = resolveSlackAttribution({
+ search: window.location.search,
+ referrer: document.referrer,
+ });
+ const eventName = buildSlackEventName(attribution);
+
+ const startedAt = Date.now();
+ const timers: number[] = [];
+ let redirected = false;
+ const redirect = () => {
+ if (redirected) {
+ return;
+ }
+ redirected = true;
+ timers.forEach(id => window.clearTimeout(id));
+ window.location.replace(slackInviteUrl);
+ };
+
+ if (process.env.NODE_ENV !== 'production') {
+ // docusaurus-plugin-matomo only injects the tracker in production
+ // builds, so surface what would have been sent.
+ console.info('[slack-redirect] Matomo event:',
SLACK_EVENT_CATEGORY, SLACK_EVENT_ACTION, eventName);
+ }
+
+ const queue = matomoWindow._paq || (matomoWindow._paq = []);
+ // sendBeacon survives the navigation triggered below; the XHR the
+ // tracker uses by default would be cancelled by it.
+ queue.push(['alwaysUseSendBeacon']);
+ queue.push([
+ 'trackEvent',
+ SLACK_EVENT_CATEGORY,
+ SLACK_EVENT_ACTION,
+ eventName,
+ undefined, // value
+ undefined, // customData
+ redirect, // invoked once the request has been handed to the
browser
+ ]);
+
+ const waitForTracker = () => {
+ const state = resolveTrackerState({
+ trackerLoaded: typeof matomoWindow.Matomo === 'object' &&
matomoWindow.Matomo !== null,
+ scriptPresent: trackerScriptUrl !== '' &&
hasScriptElement(trackerScriptUrl),
+ fetchFinished: trackerScriptUrl !== '' &&
hasFinishedFetching(trackerScriptUrl),
+ });
+
+ if (state === 'loaded') {
+ // The event callback takes it from here; the hard cap below
+ // still applies in case the tracker never sends anything.
+ return;
+ }
+ if (state === 'absent' || state === 'failed' || Date.now() -
startedAt >= TRACKER_WAIT_MS) {
+ redirect();
+ return;
+ }
+ timers.push(window.setTimeout(waitForTracker, TRACKER_POLL_MS));
+ };
+
+ waitForTracker();
+ timers.push(window.setTimeout(redirect, REDIRECT_MAX_WAIT_MS));
+
+ return () => timers.forEach(id => window.clearTimeout(id));
+ }, [slackInviteUrl, trackerScriptUrl]);
+
+ return (
+ <>
+ <Head>
+ <title>Join the Apache Doris Slack community</title>
+ <meta name="robots" content="noindex, nofollow" />
+ <meta name="description" content="Forwarding you to the Apache
Doris community Slack workspace." />
+ </Head>
+ <main className="slack-redirect">
+ {/* Browsers without JavaScript never run the effect above; the
+ server-rendered meta refresh forwards them straight away.
*/}
+ <noscript>
+ <meta httpEquiv="refresh"
content={`0;url=${slackInviteUrl}`} />
+ </noscript>
+ <div className="slack-redirect__card" role="status"
aria-live="polite">
+ <div className="slack-redirect__icon" aria-hidden="true">
+ <SlackIcon />
+ </div>
+ <p className="slack-redirect__eyebrow">Apache Doris
Community</p>
+ <h1 className="slack-redirect__title">Taking you to
Slack…</h1>
+ <p className="slack-redirect__copy">
+ If nothing happens within a few seconds,{' '}
+ <a className="slack-redirect__link"
href={slackInviteUrl}>
+ open the Slack invite
+ </a>{' '}
+ directly.
+ </p>
+ <div className="slack-redirect__progress"
aria-hidden="true">
+ <span className="slack-redirect__progress-bar" />
+ </div>
+ </div>
+ </main>
+ </>
+ );
+}
diff --git a/src/components/slack-redirect/slack-attribution.logic.d.ts
b/src/components/slack-redirect/slack-attribution.logic.d.ts
new file mode 100644
index 00000000000..c17a57cfe2f
--- /dev/null
+++ b/src/components/slack-redirect/slack-attribution.logic.d.ts
@@ -0,0 +1,64 @@
+export const SLACK_ENTRY_URL: string;
+export const SLACK_EVENT_CATEGORY: string;
+export const SLACK_EVENT_ACTION: string;
+
+/** utm_medium values the website itself is allowed to emit. */
+export type SlackEntryMedium = 'website' | 'docs';
+
+export interface SlackEntryUrlOptions {
+ medium: SlackEntryMedium;
+ /** Which CTA the visitor clicked, e.g. `homepage_hero`, `footer`,
`docs_toc`. */
+ content: string;
+ campaign?: string;
+}
+
+export type SlackAttributionOrigin = 'utm' | 'referrer' | 'direct';
+
+export interface SlackAttributionInput {
+ /** `window.location.search`, including the leading `?`. */
+ search: string;
+ /** `document.referrer`; may be empty. */
+ referrer: string;
+}
+
+export interface SlackAttribution {
+ attributedBy: SlackAttributionOrigin;
+ source: string;
+ medium: string;
+ campaign: string;
+ content: string;
+ /** Referrer hostname without `www.`, or an empty string. */
+ referrerHost: string;
+}
+
+export function sanitizeAttributionValue(value: unknown): string;
+
+export function getReferrerHost(referrer: string): string;
+
+export function resolveSlackAttribution(input: SlackAttributionInput):
SlackAttribution;
+
+export function buildSlackEventName(
+ attribution: Pick<SlackAttribution, 'source' | 'medium' | 'campaign' |
'content'>,
+): string;
+
+export function buildSlackEntryUrl(options: SlackEntryUrlOptions): string;
+
+export interface MatomoThemeConfig {
+ matomoUrl?: string;
+ jsLoader?: string;
+}
+
+export function getMatomoScriptUrl(matomoConfig: MatomoThemeConfig |
undefined): string;
+
+export type TrackerState = 'loaded' | 'absent' | 'failed' | 'pending';
+
+export interface TrackerStateInput {
+ /** `window.Matomo` exists, i.e. matomo.js has executed. */
+ trackerLoaded: boolean;
+ /** A `<script src=matomo.js>` element is on the page. */
+ scriptPresent: boolean;
+ /** Resource Timing reports the script fetch as finished (success or
failure). */
+ fetchFinished: boolean;
+}
+
+export function resolveTrackerState(input: TrackerStateInput): TrackerState;
diff --git a/src/components/slack-redirect/slack-attribution.logic.js
b/src/components/slack-redirect/slack-attribution.logic.js
new file mode 100644
index 00000000000..e0f4e02358f
--- /dev/null
+++ b/src/components/slack-redirect/slack-attribution.logic.js
@@ -0,0 +1,170 @@
+// Attribution helpers shared by the /slack tracking page
+// (src/components/slack-redirect/SlackRedirect.tsx) and every Slack entry
+// point the website renders itself (hero, footer, docs TOC, mascot nudge,
+// community docs).
+//
+// Plain CommonJS on purpose: the same file runs in the browser bundle and
+// under `node --test` (see slack-attribution.logic.test.js), and the sibling
+// .d.ts provides the TypeScript surface. Same pattern as
+// SlackCommunityNudge.logic.js. The naming rules are documented in
+// developer_docs/slack-utm-convention.md.
+
+// Public entry point every channel links to. The real Slack invite URL lives
+// in docusaurus.config.js (customFields.slackInviteUrl) so it can be rotated
+// in one place without touching any channel.
+const SLACK_ENTRY_URL = 'https://doris.apache.org/slack';
+
+// Matomo event taxonomy. The Events report groups on category/action, so
+// renaming either one splits the history - keep them stable.
+const SLACK_EVENT_CATEGORY = 'Community Acquisition';
+const SLACK_EVENT_ACTION = 'Slack Click';
+
+// Event name layout: source|medium|campaign|content. Values are sanitised so
+// the separator can never appear inside a field.
+const ATTRIBUTION_FIELD_SEPARATOR = '|';
+const ATTRIBUTION_VALUE_MAX_LENGTH = 64;
+
+// utm_source carried by every link the website renders itself.
+const WEBSITE_UTM_SOURCE = 'website';
+
+// Synthetic values used when a click carried no utm_source.
+const REFERRAL_MEDIUM = 'referral';
+const DIRECT_SOURCE = 'direct';
+const DIRECT_MEDIUM = 'none';
+
+function sanitizeAttributionValue(value) {
+ if (typeof value !== 'string') {
+ return '';
+ }
+
+ return value
+ .trim()
+ .toLowerCase()
+ .replace(/[^a-z0-9._-]+/g, '_')
+ .replace(/^_+|_+$/g, '')
+ .slice(0, ATTRIBUTION_VALUE_MAX_LENGTH);
+}
+
+function getReferrerHost(referrer) {
+ if (typeof referrer !== 'string' || referrer === '') {
+ return '';
+ }
+
+ try {
+ return new URL(referrer).hostname.toLowerCase().replace(/^www\./, '');
+ } catch (error) {
+ return '';
+ }
+}
+
+function parseUtmParams(search) {
+ const params = new URLSearchParams(typeof search === 'string' ? search :
'');
+
+ return {
+ source: sanitizeAttributionValue(params.get('utm_source')),
+ medium: sanitizeAttributionValue(params.get('utm_medium')),
+ campaign: sanitizeAttributionValue(params.get('utm_campaign')),
+ content: sanitizeAttributionValue(params.get('utm_content')),
+ };
+}
+
+// Attribution priority: utm_source > document.referrer > direct. Campaign and
+// content are passed through whenever they are present so a link that forgot
+// utm_source still keeps whatever it did carry.
+function resolveSlackAttribution({ search, referrer }) {
+ const utm = parseUtmParams(search);
+ const referrerHost = sanitizeAttributionValue(getReferrerHost(referrer));
+
+ if (utm.source) {
+ return {
+ attributedBy: 'utm',
+ source: utm.source,
+ medium: utm.medium,
+ campaign: utm.campaign,
+ content: utm.content,
+ referrerHost,
+ };
+ }
+
+ if (referrerHost) {
+ return {
+ attributedBy: 'referrer',
+ source: referrerHost,
+ medium: utm.medium || REFERRAL_MEDIUM,
+ campaign: utm.campaign,
+ content: utm.content,
+ referrerHost,
+ };
+ }
+
+ return {
+ attributedBy: 'direct',
+ source: DIRECT_SOURCE,
+ medium: utm.medium || DIRECT_MEDIUM,
+ campaign: utm.campaign,
+ content: utm.content,
+ referrerHost,
+ };
+}
+
+function buildSlackEventName({ source, medium, campaign, content }) {
+ return [source, medium, campaign, content]
+ .map(value => sanitizeAttributionValue(value))
+ .join(ATTRIBUTION_FIELD_SEPARATOR);
+}
+
+// Where docusaurus-plugin-matomo loads the tracker from. Mirrors the plugin's
+// own string concatenation so the result matches the injected <script src>.
+function getMatomoScriptUrl(matomoConfig) {
+ if (!matomoConfig || typeof matomoConfig.matomoUrl !== 'string' ||
matomoConfig.matomoUrl === '') {
+ return '';
+ }
+
+ return `${matomoConfig.matomoUrl}${matomoConfig.jsLoader || 'matomo.js'}`;
+}
+
+// What the /slack page is waiting for:
+// loaded - matomo.js has executed; the queued event goes out and its
+// callback performs the redirect
+// absent - no tracker script on this page (development builds); nothing
+// to wait for
+// failed - the script fetch finished without producing a tracker (ad
+// blocker, 404, network error); nothing to wait for either
+// pending - the script is still being fetched
+function resolveTrackerState({ trackerLoaded, scriptPresent, fetchFinished }) {
+ if (trackerLoaded) {
+ return 'loaded';
+ }
+ if (!scriptPresent) {
+ return 'absent';
+ }
+ if (fetchFinished) {
+ return 'failed';
+ }
+ return 'pending';
+}
+
+function buildSlackEntryUrl({ medium, content, campaign }) {
+ const params = new URLSearchParams();
+ params.set('utm_source', WEBSITE_UTM_SOURCE);
+ params.set('utm_medium', medium);
+ if (campaign) {
+ params.set('utm_campaign', campaign);
+ }
+ params.set('utm_content', content);
+
+ return `${SLACK_ENTRY_URL}?${params.toString()}`;
+}
+
+module.exports = {
+ SLACK_ENTRY_URL,
+ SLACK_EVENT_ACTION,
+ SLACK_EVENT_CATEGORY,
+ buildSlackEntryUrl,
+ buildSlackEventName,
+ getMatomoScriptUrl,
+ getReferrerHost,
+ resolveSlackAttribution,
+ resolveTrackerState,
+ sanitizeAttributionValue,
+};
diff --git a/src/components/slack-redirect/slack-attribution.logic.test.js
b/src/components/slack-redirect/slack-attribution.logic.test.js
new file mode 100644
index 00000000000..c37ffd56ca9
--- /dev/null
+++ b/src/components/slack-redirect/slack-attribution.logic.test.js
@@ -0,0 +1,136 @@
+const assert = require('node:assert/strict');
+const test = require('node:test');
+
+const {
+ SLACK_ENTRY_URL,
+ buildSlackEntryUrl,
+ buildSlackEventName,
+ getMatomoScriptUrl,
+ getReferrerHost,
+ resolveSlackAttribution,
+ resolveTrackerState,
+ sanitizeAttributionValue,
+} = require('./slack-attribution.logic');
+
+test('builds website entry links with the documented parameter order', () => {
+ assert.equal(
+ buildSlackEntryUrl({ medium: 'website', content: 'homepage_hero' }),
+
`${SLACK_ENTRY_URL}?utm_source=website&utm_medium=website&utm_content=homepage_hero`,
+ );
+ assert.equal(
+ buildSlackEntryUrl({ medium: 'docs', content: 'docs_toc', campaign:
'release_4_1' }),
+
`${SLACK_ENTRY_URL}?utm_source=website&utm_medium=docs&utm_campaign=release_4_1&utm_content=docs_toc`,
+ );
+});
+
+test('prefers UTM parameters over the referrer', () => {
+ const attribution = resolveSlackAttribution({
+ search:
'?utm_source=github&utm_medium=community&utm_content=github_readme',
+ referrer: 'https://github.com/apache/doris',
+ });
+
+ assert.deepEqual(attribution, {
+ attributedBy: 'utm',
+ source: 'github',
+ medium: 'community',
+ campaign: '',
+ content: 'github_readme',
+ referrerHost: 'github.com',
+ });
+ assert.equal(buildSlackEventName(attribution),
'github|community||github_readme');
+});
+
+test('keeps every UTM field in the event name', () => {
+ const attribution = resolveSlackAttribution({
+ search:
'?utm_source=linkedin&utm_medium=social&utm_campaign=community_growth_h2_2026&utm_content=post_01',
+ referrer: '',
+ });
+
+ assert.equal(buildSlackEventName(attribution),
'linkedin|social|community_growth_h2_2026|post_01');
+});
+
+test('falls back to the referrer host when no utm_source is present', () => {
+ const attribution = resolveSlackAttribution({
+ search: '',
+ referrer:
'https://www.reddit.com/r/dataengineering/comments/abc/apache_doris/',
+ });
+
+ assert.equal(attribution.attributedBy, 'referrer');
+ assert.equal(attribution.source, 'reddit.com');
+ assert.equal(attribution.medium, 'referral');
+ assert.equal(buildSlackEventName(attribution), 'reddit.com|referral||');
+});
+
+test('records direct traffic when neither UTM nor referrer is available', ()
=> {
+ const attribution = resolveSlackAttribution({ search: '', referrer: '' });
+
+ assert.equal(attribution.attributedBy, 'direct');
+ assert.equal(buildSlackEventName(attribution), 'direct|none||');
+});
+
+test('keeps campaign and content from a link that forgot utm_source', () => {
+ const withReferrer = resolveSlackAttribution({
+ search:
'?utm_campaign=iceberg_webinar_202609&utm_content=closing_slide',
+ referrer: 'https://zoom.us/webinar/123',
+ });
+ assert.equal(buildSlackEventName(withReferrer),
'zoom.us|referral|iceberg_webinar_202609|closing_slide');
+
+ const direct = resolveSlackAttribution({
+ search: '?utm_medium=offline&utm_campaign=awsug_uzbekistan_2026',
+ referrer: '',
+ });
+ assert.equal(buildSlackEventName(direct),
'direct|offline|awsug_uzbekistan_2026|');
+});
+
+test('ignores referrers that are not valid URLs', () => {
+ assert.equal(getReferrerHost('not a url'), '');
+ assert.equal(getReferrerHost(''), '');
+ assert.equal(getReferrerHost(undefined), '');
+ assert.equal(resolveSlackAttribution({ search: '', referrer: 'not a url'
}).attributedBy, 'direct');
+});
+
+test('normalises referrer hosts', () => {
+ assert.equal(getReferrerHost('https://WWW.Medium.com/@someone/post'),
'medium.com');
+ assert.equal(getReferrerHost('android-app://com.slack/'), 'com.slack');
+});
+
+test('sanitises values so the separator and stray characters never leak into
the event name', () => {
+ assert.equal(sanitizeAttributionValue(' Git Hub|README '),
'git_hub_readme');
+ assert.equal(sanitizeAttributionValue('Doris Summit 2026!'),
'doris_summit_2026');
+ assert.equal(sanitizeAttributionValue('__already-clean.value__'),
'already-clean.value');
+ assert.equal(sanitizeAttributionValue(null), '');
+ assert.equal(sanitizeAttributionValue(42), '');
+ assert.equal(sanitizeAttributionValue('a'.repeat(100)).length, 64);
+
+ const attribution = resolveSlackAttribution({
+ search: '?utm_source=Some%20Blog%7Ctest&utm_medium=CONTENT',
+ referrer: '',
+ });
+ const eventName = buildSlackEventName(attribution);
+ assert.equal(eventName, 'some_blog_test|content||');
+ assert.equal(eventName.split('|').length, 4);
+});
+
+test('buildSlackEventName sanitises raw input as well', () => {
+ assert.equal(
+ buildSlackEventName({ source: 'Web|Site', medium: 'Docs ', campaign:
'', content: 'Footer' }),
+ 'web_site|docs||footer',
+ );
+});
+
+test('derives the tracker script URL the way docusaurus-plugin-matomo does',
() => {
+ assert.equal(getMatomoScriptUrl({ matomoUrl:
'https://analytics.apache.org/', jsLoader: 'matomo.js' }),
'https://analytics.apache.org/matomo.js');
+ assert.equal(getMatomoScriptUrl({ matomoUrl:
'https://analytics.apache.org/' }), 'https://analytics.apache.org/matomo.js');
+ assert.equal(getMatomoScriptUrl({ matomoUrl:
'https://analytics.apache.org/', jsLoader: 'piwik.js' }),
'https://analytics.apache.org/piwik.js');
+ assert.equal(getMatomoScriptUrl(undefined), '');
+ assert.equal(getMatomoScriptUrl({}), '');
+});
+
+test('classifies what the redirect page is waiting for', () => {
+ assert.equal(resolveTrackerState({ trackerLoaded: true, scriptPresent:
true, fetchFinished: true }), 'loaded');
+ assert.equal(resolveTrackerState({ trackerLoaded: false, scriptPresent:
false, fetchFinished: false }), 'absent');
+ assert.equal(resolveTrackerState({ trackerLoaded: false, scriptPresent:
true, fetchFinished: true }), 'failed');
+ assert.equal(resolveTrackerState({ trackerLoaded: false, scriptPresent:
true, fetchFinished: false }), 'pending');
+ // A tracker that is already up wins regardless of the other signals.
+ assert.equal(resolveTrackerState({ trackerLoaded: true, scriptPresent:
false, fetchFinished: false }), 'loaded');
+});
diff --git a/src/pages/slack.tsx b/src/pages/slack.tsx
new file mode 100644
index 00000000000..3b035f6925b
--- /dev/null
+++ b/src/pages/slack.tsx
@@ -0,0 +1,10 @@
+import React, { JSX } from 'react';
+import SlackRedirect from '@site/src/components/slack-redirect/SlackRedirect';
+
+// https://doris.apache.org/slack is the single public entry point to the
+// community Slack. The page records the click (UTM + referrer) in the ASF
+// Matomo instance and then forwards to the real invite link configured in
+// docusaurus.config.js (customFields.slackInviteUrl).
+export default function SlackPage(): JSX.Element {
+ return <SlackRedirect />;
+}
diff --git a/src/theme/Footer/index.tsx b/src/theme/Footer/index.tsx
index 96a16f61258..a5059333fc2 100644
--- a/src/theme/Footer/index.tsx
+++ b/src/theme/Footer/index.tsx
@@ -11,6 +11,7 @@ import { MailIcon } from '@site/src/components/Icons/mail';
import { GithubIcon } from '@site/src/components/Icons/github';
import { TwitterIcon } from '@site/src/components/Icons/twitter';
import { SlackIcon } from '@site/src/components/Icons/slack';
+import { buildSlackEntryUrl } from
'@site/src/components/slack-redirect/slack-attribution.logic';
import { DiscordIcon } from '@site/src/components/Icons/discord-icon';
import { BilibiliIcon } from '@site/src/components/Icons/bilibili';
import { WechatIcon } from '@site/src/components/Icons/wechat';
@@ -93,7 +94,7 @@ function Footer(): React.ReactElement | null {
<TwitterIcon />
</a>
<a
- href="https://doris.apache.org/slack"
+ href={buildSlackEntryUrl({ medium:
'website', content: 'footer' })}
title="slack"
target="_blank"
className="item"
diff --git a/src/theme/TOC/index.tsx b/src/theme/TOC/index.tsx
index adfd14699b1..e5deab2e646 100644
--- a/src/theme/TOC/index.tsx
+++ b/src/theme/TOC/index.tsx
@@ -13,6 +13,7 @@ import { DOWNLOAD_PDFS } from
'@site/src/constant/download.data';
import { VERSIONS, DEFAULT_VERSION } from '@site/src/constant/version';
import { DiscordIconSmall } from '../../components/Icons/discord-icon-small';
import { normalizePathname } from '@site/src/utils/locale';
+import { buildSlackEntryUrl } from
'@site/src/components/slack-redirect/slack-attribution.logic';
import { Spin } from 'antd';
import Link from '@docusaurus/Link';
@@ -135,9 +136,7 @@ export default function TOC({ className, ...props }:
Props): React.ReactElement
{!isCN ? (
<Link
className="toc-icon-content group"
- to={
- 'https://doris.apache.org/slack'
- }
+ to={buildSlackEntryUrl({ medium: 'docs', content:
'docs_toc' })}
>
<div style={{ padding: '2px' }}>
<SlackIcon className="group-hover:text-primary" />
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]