今日已更新 208 条资讯 | 累计 37759 条内容
关于我们

标签:#tor

找到 1091 篇相关文章

AI 资讯

Why AI Will Not Replace Teachers, But It Will Change the Way Students Learn

Artificial intelligence has become one of the most discussed technologies in education. From automated grading systems to AI chatbots capable of answering complex questions, many people wonder whether AI will eventually replace teachers. The short answer is no. Education has never been just about delivering information. Great teachers inspire curiosity, understand students' emotions, adapt to different learning styles, and create environments where learners develop critical thinking. These are deeply human abilities that artificial intelligence cannot fully replicate. However, AI is beginning to solve a different problem: helping students learn independently outside the classroom. The Problem With Traditional Self-Study Many students spend hours reading textbooks without truly understanding the concepts. When they encounter a difficult paragraph, they often search the internet, only to find lengthy articles, conflicting explanations, or answers that are either too advanced or completely unrelated to their curriculum. This creates an inefficient learning process where students spend more time searching than actually learning. Another common challenge is passive learning. Reading a chapter once often creates the illusion of understanding, but without testing knowledge through questions or applying concepts, much of that information is quickly forgotten. How AI Can Support Learning Modern educational AI systems are becoming less like search engines and more like interactive learning companions. Instead of simply returning search results, these systems can explain concepts in simpler language, adapt explanations to a student's academic level, answer follow-up questions, generate practice quizzes, and even identify areas where additional practice is needed. This creates a much more personalized learning experience. Learning From Personal Study Materials One of the most interesting developments in AI education is the ability to work with a student's own resources. Rather

2026-07-08 原文 →
AI 资讯

Integrating Git Submodules the Easy Way

Git submodules have a reputation for being fiddly, but most of that pain comes down to a handful of missing commands and one config flag nobody mentions. Used well, they're a clean way to embed a shared library, a design-system repo, or a common docs folder inside another project - pinned to an exact commit so nothing shifts under your feet. This guide walks through the whole lifecycle, from adding a submodule to removing it, and calls out the gotchas that bite teams in real projects. Understanding What a Submodule Actually Is Before the commands, one mental model that clears up most confusion: a submodule embeds another git repo inside yours at a fixed path, pinned to a specific commit. Your repo doesn't track the submodule's files - it tracks which commit of the submodule to check out. That single idea explains almost every quirk that follows. Adding a Submodule Adding one is a single command: git submodule add git@github.com:org/shared-lib.git vendor/shared-lib This clones the repo into vendor/shared-lib , creates a .gitmodules file describing the mapping, and stages the pinned commit (git calls this a "gitlink"). Commit both pieces: git add .gitmodules vendor/shared-lib git commit -m "chore: add shared-lib submodule" The resulting .gitmodules entry is plain text and lives in version control: [submodule "vendor/shared-lib"] path = vendor/shared-lib url = git@github.com:org/shared-lib.git branch = main The branch line is optional: it's only used later when pulling the latest changes automatically. Cloning Without the Empty-Folder Surprise The most common submodule complaint is a teammate cloning the project and finding an empty folder where the submodule should be. The fix is knowing two commands: # Clone everything in one shot git clone --recurse-submodules <your-repo-url> # Already cloned? Initialize after the fact git submodule update --init --recursive Even better, run this once per machine so git pull and git checkout keep submodules in sync automatically - a

2026-07-08 原文 →
AI 资讯

Make your content answer-first so AI models actually cite it

If you want ChatGPT or Google's AI Overviews to quote your pages, structure matters more than volume. Retrieval systems favor passages where the answer is stated plainly and can stand alone. Here's a practical way to test and fix your content. Step 1 — Define the question the page answers Write it as a literal user query. How much does a website cost for a small business in the UK? Step 2 — Extract your current answer passage Copy the first two or three sentences from your page. Paste them somewhere without any extra context. Ask yourself: Does this work as a direct answer? If it only makes sense after reading earlier paragraphs, it doesn’t pass the extraction test. Step 3 — Rewrite answer-first Lead with the conclusion, stated as a fact, then support it. Before: "We get asked about pricing a lot, and honestly it's one of the trickiest questions to answer..." After: "A small-business website in the UK typically costs £1,500–£6,000 for a brochure site and £6,000–£20,000+ for e-commerce. The price depends on three things: page count, payment functionality, and custom vs template design." Step 4 — Test extractability with a model Send the passage to an LLM and check whether it returns a clean, single answer. Use a system prompt that mimics retrieval behavior. System: You are a retrieval system. From the passage below, extract the single most direct answer to the user's question. If no self-contained answer exists, reply "NO_EXTRACTABLE_ANSWER". User question: How much does a website cost for a small business in the UK? Passage: If you get NO_EXTRACTABLE_ANSWER or a vague summary, your structure needs work. Step 5 — Reinforce with structured data Markup question and answer pages with FAQPage schema so the question/answer pairing is machine-readable as well as human-readable. json { " @context ": " https://schema.org ", "@type": "FAQPage", "mainEntity": [{ "@type": "Question", "name": "How much does a website cost for a small business in the UK?", "acceptedAnswer": { "@t

2026-07-08 原文 →
AI 资讯

دليل عملي لاختبار التحميل لواجهات API باستخدام أرتيلري

Artillery هي مجموعة أدوات مفتوحة المصدر لاختبار التحميل مبنية على Node.js. تتيح لك توليد حركة مرور عالية التزامن على واجهة برمجة التطبيقات (API) من خلال ملف YAML بسيط: تحدد مراحل التحميل، تصف تدفقات الطلبات، تشغل artillery run script.yml ، ثم تقرأ نسب زمن الاستجابة المئوية، معدلات الطلبات، وعدد الأخطاء. يشرح هذا الدليل طريقة تثبيت Artillery v2، كتابة اختبار عملي، تشغيله، استخراج النتائج بالطريقة الصحيحة في v2، وربطه بمسار CI. جرّب Apidog اليوم ما هو Artillery ومتى تستخدمه؟ ينشئ Artillery مستخدمين افتراضيين (VUs) يرسلون طلبات إلى نقاط النهاية لديك ويقيسون قدرة النظام على تحمل الحمل المستمر. المستخدم الافتراضي هو عميل مُحاكى ينفذ سيناريو خطوة بخطوة، كما يفعل مستخدم أو خدمة حقيقية. استخدم Artillery عندما تريد إجابات عملية على أسئلة الأداء مثل: كيف يتغير زمن الاستجابة p95 عند 50 طلبًا في الثانية؟ عند أي معدل وصول تبدأ الأخطاء بالظهور؟ هل تبقى واجهة API مستقرة لمدة 5 دقائق من الحمل المستمر؟ هل يتدهور الأداء تدريجيًا مع استمرار الضغط؟ الميزة الأساسية في Artillery أن الاختبار تصريحي. بدل كتابة حلقات تزامن يدويًا، تصف شكل الحمل في YAML. وبما أنه يعمل فوق Node.js، يمكنك تشغيل نفس الاختبار محليًا وفي CI. إذا كنت تقارن الأدوات، راجع ملخص أفضل أدوات اختبار التحميل و مقارنة برامج اختبار التحميل لفهم الفروقات بين k6 وJMeter وGatling وغيرها. تثبيت Artillery v2 اسم الحزمة هو artillery ، والإصدار الرئيسي الحالي هو v2. ثبته عالميًا عبر npm: npm install -g artillery@latest artillery version تحتاج إلى إصدار LTS حديث من Node.js. يعمل Artillery على Windows وmacOS وLinux. إذا كنت لا تريد تثبيت الحزمة عالميًا، استخدم npx : npx artillery@latest run script.yml كتابة اختبار Artillery يتكون ملف الاختبار من قسمين أساسيين: config : يحدد الهدف ومراحل الحمل والمتغيرات. scenarios : يحدد ما يفعله كل مستخدم افتراضي. مثال كامل: config : target : " https://api.example.com" phases : - name : " Warm up" duration : 60 arrivalRate : 5 - name : " Ramp to peak" duration : 120 arrivalRate : 5 rampTo : 50 - name : " Sustained load" duration : 300 arrivalRate : 50 maxVusers : 500 variables : productId : - " 100

2026-07-08 原文 →
AI 资讯

Hướng dẫn chạy kiểm thử API Apidog CLI trên Drone CI

Bạn có thể chạy bài kiểm tra API của Apidog CLI trong Drone CI bằng một bước Docker dùng ảnh Node, cài apidog-cli , rồi gọi apidog run với test scenario và environment tương ứng. Token Apidog nên được lưu trong Drone Secret và inject vào pipeline bằng from_secret . Bài viết này cung cấp cấu hình .drone.yml có thể copy-paste, cách quản lý secret, giới hạn chạy theo branch/event và cách xuất báo cáo khi Drone không có artifact storage tích hợp sẵn. Dùng thử Apidog ngay hôm nay Drone CI là gì và hoạt động như thế nào Drone là nền tảng CI/CD mã nguồn mở, chạy theo mô hình container-native và hiện là một phần của Harness. CI/CD là thực hành tự động build, test và phân phối phần mềm trên mỗi thay đổi mã nguồn. Nếu cần ôn lại nền tảng, xem thêm CI/CD là gì . Điểm quan trọng của Drone: mỗi step trong pipeline chạy trong một Docker container riêng. Bạn chọn image cho từng step, sau đó Drone chạy các command trong container đó. Cách này giúp pipeline dễ tái tạo, dễ debug và không phụ thuộc vào một build agent đã cài sẵn nhiều công cụ. Pipeline được khai báo trong file .drone.yml ở thư mục gốc repository. Một Docker pipeline thường có: kind type name steps Ví dụ tối thiểu: kind : pipeline type : docker name : api-tests steps : - name : greeting image : alpine commands : - echo hello - echo world Drone chạy commands như shell script với cơ chế fail-fast. Nếu một command trả về exit code khác 0 , build sẽ fail. Working directory mặc định là thư mục gốc của repository. Vì sao nên chạy API test trong container step API test giúp phát hiện sớm các thay đổi phá vỡ contract, ví dụ: response schema thay đổi ngoài ý muốn status code khác kỳ vọng field bắt buộc bị thiếu logic xác thực hoặc phân quyền bị lỗi Với Apidog, bạn thiết kế và duy trì test scenario trong UI, sau đó chạy lại chính các scenario đó từ CLI. CI không cần giữ thêm collection file riêng hoặc viết lại test script. Để xem thêm về cách đưa API testing vào pipeline, đọc các phương pháp hay nhất về CI/CD cho kiểm tra API .

2026-07-08 原文 →
AI 资讯

Set Up Ollama with OpenClaw: Run Local AI Models Inside Agent Workflows

AI agents are not useful just because they can answer prompts. They become useful when they can work with tools, files, workflows, commands, and real project context. That is why pairing Ollama with OpenClaw makes sense. Ollama lets you run local AI models. OpenClaw gives those models a practical agent workflow layer, so you can test how local models behave in something closer to a real working setup. What You Will Set Up In this guide, you will set up: Ollama for running local models A local model such as Mistral or Llama OpenClaw for agent workflow control The OpenClaw gateway and dashboard A basic local-first AI agent setup The goal is simple: run local models inside an agent workflow instead of only testing them in a chat window. Why Use Ollama with OpenClaw? Most local model testing looks like this: ollama run mistral That is fine for checking whether a model responds. But agent workflows need more than a response. They need: tool access project context file awareness safe execution repeatable workflows a dashboard or control layer OpenClaw helps with that agent workflow layer. So instead of asking: Can this model answer a prompt? You can test: Can this model actually work inside my AI agent workflow? That is a much better question. Step 1: Install Ollama First, install Ollama on your machine. After installation, check that it is working: ollama list If Ollama is not running, start it: ollama serve You can also test the local API: curl http://127.0.0.1:11434/api/tags If you get a response, Ollama is running correctly. Step 2: Pull a Local Model Now pull a model. For basic testing: ollama pull mistral Then run it: ollama run mistral You can use another model if your machine has enough resources. For simple testing, smaller models are fine. For coding, planning, and multi-step agent tasks, stronger models usually perform better. Tiny models are cheap and fast, but expecting them to behave like senior engineers is how humans invent disappointment at scale. Step 3:

2026-07-08 原文 →
AI 资讯

Build an AI dubbing pipeline: faster-whisper + XTTS-v2 + FFmpeg

TL;DR We're building a script that takes a video in English and produces the same video narrated in Spanish, in a cloned version of the original speaker's voice. Stack: faster-whisper for timestamped transcription, an LLM (or any MT engine) for translation, XTTS-v2 for voice-cloned synthesis, FFmpeg for surgery. We'll also handle the problem every demo skips: translated audio that doesn't fit its time slot. 📦 Code: github.com/USER/repo (replace before publishing) If you'd rather start from a finished system, Softcatala's open-dubbing and KrillinAI are full pipelines behind one CLI. This post builds the minimal version by hand so you understand what those tools are doing, and where they break. 0. Setup and a licensing warning ⚠️ Python 3.10–3.12. The original Coqui company shut down in early 2024; the maintained fork of their TTS library is published by Idiap as coqui-tts : $ python -m venv dub && source dub/bin/activate $ pip install faster-whisper coqui-tts $ ffmpeg -version | head -1 # 6.0+ is fine, 8.x current ⚠️ Note: the XTTS-v2 model weights ship under the Coqui Public Model License, which restricts commercial use. Prototype freely, but before dubbed videos ship to paying customers, someone must read that license and possibly swap the synthesis step for a commercially licensed model or paid API. Voice cloning also requires the speaker's consent. Get it in writing. 1. Extract audio and transcribe with word timestamps 🎙️ # pull mono 16k audio for the ASR step $ ffmpeg -i input.mp4 -vn -ac 1 -ar 16000 -y source.wav # dub/transcribe.py from faster_whisper import WhisperModel model = WhisperModel ( " large-v3-turbo " , compute_type = " int8 " ) segments , info = model . transcribe ( " source.wav " , word_timestamps = True ) lines = [] for seg in segments : lines . append ({ " start " : seg . start , " end " : seg . end , " text " : seg . text . strip (), }) print ( f " language= { info . language } segments= { len ( lines ) } " ) The timestamps are the skeleton of

2026-07-08 原文 →
AI 资讯

Probing FFmpeg's av1_vulkan encoder: does your GPU actually support it?

TL;DR FFmpeg 8.x includes av1_vulkan , the first cross-vendor GPU AV1 encoder in mainline FFmpeg. We'll probe whether your GPU + driver actually expose AV1 encode, run a first working encode, benchmark it against SVT-AV1 on your own content, and talk about which jobs deserve it. 📦 Code: github.com/USER/repo (replace before publishing) Until FFmpeg 8.0 ("Huffman", released August 2025), GPU AV1 encoding meant picking a vendor: av1_nvenc for NVIDIA RTX 40+, av1_amf for AMD, av1_qsv for Intel Arc. Three code paths, three sets of flags, three driver stacks. The Vulkan Video encode work gives FFmpeg one encoder that reaches all three vendors through the standard VK_KHR_video_encode_av1 extension. The catch: driver support is a lottery. Plenty of capable hardware sits behind drivers that don't expose the encode extension yet. So before any pipeline decisions, we probe. 1. Check what you're running You want FFmpeg 8.x (8.1.2 is current as of late June 2026) built with Vulkan support, plus the vulkaninfo tool from the Vulkan SDK / vulkan-tools package. $ ffmpeg -version | head -1 ffmpeg version 8.1.2 Copyright ( c ) 2000-2026 the FFmpeg developers $ ffmpeg -hide_banner -encoders | grep vulkan V....D av1_vulkan AV1 ( Vulkan ) ( codec av1 ) If av1_vulkan doesn't appear, your build wasn't compiled with --enable-vulkan (distro packages vary; the BtbN static builds and most 8.x distro packages include it). 2. Probe the driver for AV1 encode 🔍 The encoder existing in FFmpeg means nothing if the driver doesn't expose the extension. This is the step that separates "should work" from "works": $ vulkaninfo | grep -iE "video_encode_(av1|queue)" VK_KHR_video_encode_av1 : extension revision 1 VK_KHR_video_encode_queue : extension revision 12 You see Meaning Both extensions listed You can encode AV1 via Vulkan 🎉 Only video_encode_queue Driver does Vulkan encode, but not AV1 (maybe H.264/H.265 only) Neither Driver too old, or GPU lacks an AV1-capable video engine Rough hardware floor: the

2026-07-08 原文 →
AI 资讯

Ship a 'Go Live' button: OBS in, LL-HLS out, webhooks in between

TL;DR We're adding live streaming to a SaaS dashboard: a backend endpoint that creates a stream, OBS as the broadcaster over RTMPS, LL-HLS playback with hls.js, and a webhook handler that keeps the UI honest. Working "go live" flow in an afternoon. 📦 Code: github.com/USER/repo (replace before publishing) Webinars, coaching sessions, company town halls: sooner or later your product gets the "can users go live?" ticket. The hard parts (ingest servers, transcoding, CDN delivery) are exactly the parts you should not build. We'll use FastPix as the managed layer here; the same flow works nearly line-for-line on Mux, Cloudflare Stream, or api.video. What we're building: A backend endpoint that creates a live stream and returns a stream key An OBS setup broadcasters can follow in two minutes A viewer page playing LL-HLS with hls.js A webhook handler that flips the webinar between scheduled → live → ended 1. Create the stream server-side 🛠️ You need API credentials (Access Token ID + Secret Key). FastPix uses Basic auth on the server API. Node 20.x, plain fetch , no SDK required (though official Node.js/Python/Go/Ruby/PHP/Java/C# SDKs exist if you prefer). // server/routes/streams.js import { Router } from " express " ; const router = Router (); const AUTH = " Basic " + Buffer . from ( ` ${ process . env . FP_TOKEN_ID } : ${ process . env . FP_SECRET } ` ). toString ( " base64 " ); router . post ( " /webinars/:id/stream " , async ( req , res ) => { const r = await fetch ( " https://api.fastpix.io/v1/live/streams " , { method : " POST " , headers : { " Content-Type " : " application/json " , Authorization : AUTH }, body : JSON . stringify ({ playbackSettings : { accessPolicy : " public " }, }), }); if ( ! r . ok ) return res . status ( 502 ). json ({ error : " stream create failed " }); const stream = await r . json (); // persist against your webinar row: // streamId, streamKey (SECRET!), playbackId await db . webinar . update ( req . params . id , { streamId : stream . str

2026-07-08 原文 →
AI 资讯

Part 1 — Container hoá app & chạy trong Kubernetes local

Bạn sẽ học gì Sau bài này, bạn sẽ tự tay đưa một app từ số 0 (một thư mục trống) đến chạy được bên trong một cluster Kubernetes chạy trên máy của bạn . Cụ thể: Viết một app Todo API nhỏ bằng Node.js + Express. Đóng gói (container hoá) nó thành một Docker image. Tạo một cluster Kubernetes local bằng kind . Deploy app bằng file YAML "thật" (không dùng lệnh tắt) để hiểu Kubernetes vận hành thế nào. Truy cập app đang chạy trong cluster từ máy của bạn. Đây là Part 1 trong series "DevOps 101 — Học K8s, Helm, ArgoCD từ số 0" . Cả series dùng chung một app tên todo-ops , các part sau sẽ xây tiếp lên nền này (thêm database, config, ingress, Helm, GitOps với ArgoCD). Điều kiện tiên quyết Docker (hoặc Docker Desktop / OrbStack) — đang chạy. kind — công cụ tạo cluster Kubernetes trong Docker. kubectl — CLI để nói chuyện với Kubernetes. Node.js 20+ và npm — để chạy thử app local. git — để quản lý mã nguồn. Cài đặt Chọn theo hệ điều hành của bạn. macOS (dùng Homebrew — nếu chưa có, cài trước): # Cài Homebrew (bỏ qua nếu đã có) /bin/bash -c " $( curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh ) " # Docker Desktop (hoặc OrbStack: brew install --cask orbstack) brew install --cask docker # Các CLI còn lại brew install kind kubectl node git Sau khi cài xong, mở Docker Desktop (hoặc OrbStack) và chờ nó báo Running trước khi chạy tiếp. Linux (Ubuntu/Debian): # Docker Engine curl -fsSL https://get.docker.com | sh sudo usermod -aG docker " $USER " # cho phép chạy docker không cần sudo (đăng xuất/đăng nhập lại để có hiệu lực) # kubectl curl -LO "https://dl.k8s.io/release/ $( curl -Ls https://dl.k8s.io/release/stable.txt ) /bin/linux/amd64/kubectl" sudo install -m 0755 kubectl /usr/local/bin/kubectl && rm kubectl # kind curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 sudo install -m 0755 kind /usr/local/bin/kind && rm kind # Node.js 20 (qua NodeSource) + git curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt-get insta

2026-07-08 原文 →
AI 资讯

The Turborepo + Bun + Biome stack behind a 40-package monorepo

Forty packages, one maintainer, and no ESLint config anywhere in the repo. That is not a boast - it is the direct result of a decision made early: every tool in the toolchain has to earn its place by governing all forty packages from one config file, not forty. The repo is flare-engine , a modular 2D engine for React Native + Web (animation, gamification, interactive UI, with games as showcases - not a game engine, not a Unity or Godot competitor). The stack behind it is Turborepo Bun Biome , and this post is the actual setup: the real turbo.json , the real biome.json , the real CI guardrail, straight from the repo (trimmed only where a config is long, and I say so where I trim), not a starter template's idealized version. Four binaries, four root configs - turbo.json , biome.json , tsconfig.base.json , and the Changesets config - each governing all forty packages at once (Bun's own "config" is just the workspaces array in the root package.json ). Plus a CI step that fails the build the moment a package imports something it shouldn't. That is the whole story, and I want to show you the files, not describe them. Four binaries, not twelve config files The thesis is narrow: a solo maintainer can keep forty packages honest only if there is exactly one config of each kind, and every package extends it rather than declaring its own variant. Twelve packages each with a slightly different ESLint config is not a monorepo, it is twelve monorepos wearing a workspace file as a costume. Here is the root package.json that runs all of it - Bun workspaces (not pnpm; that distinction matters and I will say it again below), the script table every package leans on, and the pinned package manager: // C:\_PROG\flare-engine-workspace\flare-engine\package.json { "name" : "flare-engine" , "version" : "0.0.0" , "private" : true , "workspaces" : [ "packages/*" , "benchmarks" , "apps/*" ], "scripts" : { "build" : "turbo build" , "test" : "turbo test" , "lint" : "turbo lint" , "typecheck" : "t

2026-07-08 原文 →
AI 资讯

A pending-plugin-count badge on the 🔌 button — reusing the dashboard cache instead of doubling state

A client asked: " After I run a cross-site update check, can each site show — right in the site list — how many plugin updates are still pending? " Visually the answer was obvious: a small red badge on the top-right of the 🔌 plugins button, like an unread-notification count. Easy to specify. The harder question was where the data comes from . We could have added a fresh API endpoint and a new cache to hold "pending count per site." But doing that would have doubled state management , and we already had a cache that knew this. We routed through the existing one. Here's the reasoning behind that decision. Reuse the dashboard cache as the data source The cross-site updates dashboard (the one we wrote about in killing the 24.5-second silence with a cache-first design ) already kept each site's pending plugins in a localStorage-backed state called _updatesDashState . Its shape: _updatesDashState = { sites : [ { site_id : " abc... " , plugins : [ {...}, {...}, {...} ] }, { site_id : " def... " , plugins : [ ... ] }, ], total_pending_count : 12 , loadedAt : 1748600000000 , } Look up by site_id , take plugins.length , and you have the badge's number. No new API, no new cache. The data that powers the cross-site dashboard is also the data that powers the site-list badge. The win of not adding state is quiet but real: When a maintenance run invalidates _updatesDashState , the badge disappears automatically (no sync code to write) The TTL (originally 7 days; later extended to 30 days with partial invalidation ) inherits from the existing design The badge and the underlying count can't drift — there's no second copy to fall out of step There's always a temptation to spin up a new endpoint for a new UI element. The rule we settled on: if the existing state answers it, don't add more. Attaching the badge — consolidate into helpers Both the list view and grid view need the same badge on the 🔌 button, so the logic lives in helpers. function _getPendingPluginCountForSite ( siteId )

2026-07-08 原文 →
AI 资讯

Deploying SFTPGo as an Azure Storage SFTP Alternative on Linux

Azure Storage SFTP is Microsoft's managed file transfer service on top of Azure Blob Storage, convenient, but billed continuously per endpoint (roughly $0.30/hour, ~$220/month) and tied to Azure AD. SFTPGo is an open-source file transfer server offering SFTP, FTP/S, and WebDAV with pluggable storage backends (local disk, Azure Blob, S3-compatible, GCS) and no per-endpoint charge. This guide deploys SFTPGo with Docker Compose and Traefik, sets up user auth (password + SSH key + 2FA), connects S3-compatible object storage, and covers the migration path from Azure Storage SFTP. By the end, you'll have a self-hosted file transfer server with the same capabilities at zero endpoint cost. Azure Storage SFTP → SFTPGo Mapping Azure Storage SFTP SFTPGo Equivalent Notes SFTP Endpoint SFTPGo SFTP Server Configurable port, default 2022 Azure Blob Storage Azure Blob backend Native support; point at the same container, no migration needed Azure AD Authentication LDAP/OIDC plugin External identity provider via plugin Local Users Web UI / REST API user management Hierarchical Namespace Virtual directories No HNS requirement Azure Monitor Built-in logging + webhooks/syslog Prerequisite: Linux server with Docker + Compose, a DNS A record for your domain, and (if migrating) an existing Azure Storage account with SFTP enabled plus the Azure CLI installed locally. Deploy with Docker Compose 1. Create the project directories: $ mkdir -p ~/sftpgo/ { data,config } $ cd ~/sftpgo 2. Create the environment file: $ nano .env DOMAIN = sftp.example.com LETSENCRYPT_EMAIL = admin@example.com 3. Create the Compose manifest: $ nano docker-compose.yml services : traefik : image : traefik:v3.6 container_name : traefik command : - " --providers.docker=true" - " --providers.docker.exposedbydefault=false" - " --entrypoints.web.address=:80" - " --entrypoints.websecure.address=:443" - " --entrypoints.web.http.redirections.entrypoint.to=websecure" - " --certificatesresolvers.letsencrypt.acme.httpchallenge=tr

2026-07-08 原文 →
AI 资讯

Replication Monitoring — HA Dashboard

HA dashboard cho Postgres replication: vì sao "có replica" không đồng nghĩa "đang HA", và cách để replica chết không âm thầm Streaming replication trong Postgres là một tunnel WAL từ primary sang standby: primary chạy một walsender cho mỗi standby, standby chạy một walreceiver nhận WAL và một startup process replay. Đau nhất trong vận hành không phải setup — mà là replica đã ngắt nhiều giờ mà không ai biết, tới lúc primary chết mới phát hiện HA thực ra là single-node từ tuần trước. HA dashboard là tập metric + alert được rút ra từ pg_stat_replication , pg_stat_wal_receiver , và pg_replication_slots để ép mọi trạng thái xấu — replica disconnect, replay đứng, slot pin WAL, sync standby biến mất — thành tín hiệu nhìn thấy được trước khi biến thành sự cố. Cơ chế hoạt động Trên primary, mỗi standby đang kết nối tạo ra một backend loại walsender — Postgres đọc WAL từ pg_wal/ (hoặc từ WAL buffers khi còn nóng) và stream qua replication connection. Trên standby, walreceiver nhận từng WAL record, ghi vào pg_wal/ local, fsync (tùy synchronous_commit ), rồi startup process apply record lên shared buffers — đây chính là replay. Bốn LSN xuất hiện trong luồng này và tương ứng với bốn cột trong pg_stat_replication : sent_lsn — byte cuối cùng primary đã gửi qua socket. write_lsn — byte cuối cùng standby đã write() vào OS page cache. flush_lsn — byte cuối cùng standby đã fsync xuống đĩa. replay_lsn — byte cuối cùng standby đã replay vào shared buffers (dữ liệu đã "thấy được" trên standby). Postgres docs quy định replay_lsn <= flush_lsn <= write_lsn <= sent_lsn <= pg_current_wal_lsn() — bốn "vạch" này chính là bốn nhịp của lag. Ba cột write_lag , flush_lag , replay_lag (kiểu interval ) là thời gian mà standby chậm hơn primary tương ứng với ba mốc write/flush/replay — được đo qua feedback message định kỳ từ standby. -- Trên primary: bức tranh đầy đủ cho một HA dashboard SELECT application_name , client_addr , state , -- streaming | catchup | startup | backup | stopping sync_state , --

2026-07-07 原文 →
AI 资讯

Write-Ahead Logging — WAL Fundamentals

WAL: vì sao Postgres bắt buộc ghi log trước data file, và lý do pg_wal/ đầy đĩa làm cluster ngừng nhận write WAL (Write-Ahead Log) là cơ chế durability lõi của Postgres: mọi thay đổi đối với heap, index, free-space map, visibility map đều phải được ghi xuống WAL và fsync trước khi data file tương ứng được phép flush ra đĩa . Nguyên tắc này, mô tả trong Postgres docs chương "Reliability and the Write-Ahead Log", là cái cho phép một transaction đã COMMIT thoả ACID-D dù OS crash hoặc mất điện ngay sau đó. Dev gặp WAL trong việc thật không phải vì cú pháp khó: gặp khi pg_wal/ đầy đĩa do một replication slot bị quên dọn, Postgres dừng nhận write với PANIC: could not write to file ... No space left on device , hoặc khi crash recovery sau OOM kéo mười mấy phút làm health check fail và load balancer cắt traffic. Cơ chế hoạt động Postgres không ghi thẳng vào data file mỗi khi có INSERT / UPDATE . Trang 8KB (heap page, index page) sống trong shared_buffers ; mỗi thay đổi tạo ra một WAL record mô tả delta đó (record type, relfilenode, block number, payload), append vào wal_buffers — một vùng shared memory nhỏ trước khi xuống đĩa. Tại thời điểm COMMIT , backend gọi XLogFlush() để write + fsync WAL tới hết byte chứa commit record; chỉ sau khi fsync trả về, Postgres mới ghi commit bit vào pg_xact và reply OK về client. Data page bẩn ở lại trong shared_buffers ; checkpointer sẽ flush chúng ra data file sau, không gắn với từng commit. WAL được tổ chức thành segment file kích thước cố định trong $PGDATA/pg_wal/ , mặc định 16MB mỗi segment (cấu hình lúc initdb --wal-segsize ). Vị trí trong WAL là LSN (Log Sequence Number) — số 64-bit, in dạng XXXX/XXXXXXXX , thực chất là byte offset từ đầu WAL của cluster. LSN tăng đơn điệu và là "đồng hồ" duy nhất Postgres tin cậy cho thứ tự ghi. -- Quan sát LSN tiến lên sau mỗi ghi SELECT pg_current_wal_lsn (); -- vd: 0/1A2B3C40 INSERT INTO t SELECT g FROM generate_series ( 1 , 1000 ) g ; SELECT pg_current_wal_lsn (); -- 0/1A2BE018 SELECT pg_wal_ls

2026-07-07 原文 →
AI 资讯

Lock Monitoring — Production Lock Analysis

Production lock analysis: vì sao pg_stat_activity một mình không đủ, và join với pg_locks mới ra root cause Lock contention trong Postgres hiếm khi báo bằng error — nó báo bằng wait_event_type = 'Lock' ở pg_stat_activity và bằng latency tăng từ phía application. Khi một incident xảy ra ("API treo, không ai biết tại sao"), thứ team cần trong 60 giây đầu là một bức tranh: PID nào đang đợi, đợi lock loại gì trên object nào, bị block bởi PID nào, PID block đó đang chạy query gì và đã giữ transaction bao lâu . pg_stat_activity một mình chỉ trả lời được nửa câu hỏi ("ai đang đợi"); pg_locks một mình chỉ trả lời nửa còn lại ("ai giữ gì"). Phải join hai view này — và bám theo pg_blocking_pids() — để dựng được blocking tree. Không có dashboard cho luồng dữ liệu này là lý do điển hình một production freeze kéo dài 30 phút thay vì 3 phút: incident commander phải mò ad-hoc bằng psql , gõ sai query, miss idle in transaction đang giữ AccessExclusiveLock của một migration nửa đời trước. Cơ chế hoạt động pg_locks là một view phơi nội dung trực tiếp của shared lock manager trong shared memory. Mỗi dòng là một lock request (đã granted hoặc đang chờ) thuộc một backend. Theo Postgres docs phần "System Views → pg_locks", các column then chốt: locktype ( relation , transactionid , tuple , virtualxid , advisory ...), relation (OID — join pg_class ), transactionid , virtualtransaction , pid (backend PID), mode ( AccessShareLock , RowExclusiveLock , ShareUpdateExclusiveLock , AccessExclusiveLock ...), granted (bool), fastpath (lock đi qua fast-path tránh shared lock manager), và waitstart (timestamp bắt đầu chờ — bổ sung sau v14, hữu ích để đo lock wait time mà không cần snapshot diff). pg_stat_activity là view phơi trạng thái runtime của mỗi backend: pid , usename , datname , application_name , client_addr , backend_start , xact_start , query_start , state ( active , idle , idle in transaction , idle in transaction (aborted) ), wait_event_type , wait_event , backend_xid , backend_xmin , qu

2026-07-07 原文 →