TCIS.Pluggable.Engine
1.0.0-rc.12
See the version list below for details.
dotnet add package TCIS.Pluggable.Engine --version 1.0.0-rc.12
NuGet\Install-Package TCIS.Pluggable.Engine -Version 1.0.0-rc.12
<PackageReference Include="TCIS.Pluggable.Engine" Version="1.0.0-rc.12" />
<PackageVersion Include="TCIS.Pluggable.Engine" Version="1.0.0-rc.12" />
<PackageReference Include="TCIS.Pluggable.Engine" />
paket add TCIS.Pluggable.Engine --version 1.0.0-rc.12
#r "nuget: TCIS.Pluggable.Engine, 1.0.0-rc.12"
#:package TCIS.Pluggable.Engine@1.0.0-rc.12
#addin nuget:?package=TCIS.Pluggable.Engine&version=1.0.0-rc.12&prerelease
#tool nuget:?package=TCIS.Pluggable.Engine&version=1.0.0-rc.12&prerelease
TCIS.Pluggable.Engine
The core engine library for the TCIS Pluggable Pipeline architecture. It provides a unified discovery mechanism (Modules + Steps + Rules + Services), Pipeline Analyzer, and Pipeline Engine.
📦 Installation
dotnet add package TCIS.Pluggable.Engine
🚀 Registration / Setup (Program.cs)
Register the Pluggable Engine and Discovery:
using TCIS.Pluggable.Engine.Modules;
var builder = WebApplication.CreateBuilder(args);
// Platform: nghiệp vụ chuẩn, dùng chung mọi Site.
builder.Services.AddPluggablePipeline(
builder.Configuration,
typeof(GatePlatformModule).Assembly
);
// Plugin đặc thù theo Site — tham chiếu TƯỜNG MINH, không quét thư mục.
builder.Services.AddSitePlugins(
builder.Configuration,
typeof(CatLaiModule).Assembly
);
Plugin được nạp bằng tham chiếu tĩnh, không phải nạp động
Mỗi bản triển khai có Host riêng, và các Host khác nhau đúng ở danh sách assembly truyền vào
AddSitePlugins:
// Cảng lớn triển khai riêng — build từ Platform + plugin của cảng đó
.AddSitePlugins(configuration, typeof(CatLaiModule).Assembly);
// Nhiều cảng nhỏ dùng chung một bản triển khai
.AddSitePlugins(configuration,
typeof(SiteAModule).Assembly,
typeof(SiteBModule).Assembly);
Vì sao không quét thư mục DLL. Tập plugin của mỗi bản triển khai đã biết tại thời điểm
build, nên AssemblyLoadContext không mua được gì — trong khi tham chiếu tĩnh mang lại thứ quan
trọng hơn nhiều: trình biên dịch kiểm tra plugin có còn khớp Core hay không.
Với nạp động, Core đổi chữ ký một interface thì plugin cũ vẫn build được (nó build riêng), và
lỗi chỉ nổ lúc chạy, trên production, của riêng cảng đó. Với tham chiếu tĩnh, build đỏ ngay và
chỉ đích danh plugin nào gãy. Đó là hàng rào rẻ nhất chống trôi dạt giữa Core và Plugin — xem md/28.
Plugin bắt buộc khai báo SiteCode khác DEFAULT. Quên đặt là lỗi âm thầm nguy hiểm nhất của
mô hình này: module sẽ được nạp cho mọi Site và ghi đè nghiệp vụ chuẩn của cả tập đoàn.
AddSitePlugins kiểm mọi module trong assembly và ném PipelineDiscoveryException lúc khởi động.
⚙️ Configuration
Cấu hình hậu tố cho việc tự động đăng ký DI trong appsettings.json:
{
"TLogSettings": {
"AopSettings": {
"DiRegistrationSuffixes": [
"Service",
"Provider",
"Mapper",
"Factory",
"Manager",
"Repository",
"Policy"
]
}
}
}
Resilience — Engine KHÔNG áp policy lên Step
Không có timeout, retry hay circuit breaker ở tầng Step. Đây là quyết định có chủ đích, không
phải thiếu sót — xem md/28.
Chính sách chịu lỗi thuộc về nơi lời gọi vượt qua ranh giới hạ tầng, không thuộc về logic nghiệp vụ chạy in-process:
| Nhu cầu | Đặt ở đâu |
|---|---|
| Giới hạn thời gian truy vấn DB | AddPluggableSqlServer<T>(commandTimeoutSeconds: 30) — thứ duy nhất thật sự huỷ được query ở phía server |
| Gọi API / gRPC ngoài | TCIS.Http đã có policy Polly riêng; gRPC dùng deadline |
| Chốt chặn cuối cho toàn request | AddRequestTimeouts của ASP.NET Core |
| Phát hiện Step nào của cảng nào đang hỏng | Số đo tcis.pipeline.step.* — xem mục dưới |
Vì sao timeout ở tầng Step không có tác dụng như người ta tưởng. Polly dùng cooperative
cancellation: nó huỷ token rồi ném ngoại lệ cho caller, nhưng Step vẫn chạy tiếp — .NET không
ép dừng được một task. Kết quả có thể là: client đã nhận lỗi, pipeline đã abort, rồi Step vẫn ghi
xuống database vài giây sau. Timeout chặn được thật chỉ nằm ở CommandTimeout và ở tầng kết nối.
Cấu hình chốt chặn cuối:
builder.Services.AddRequestTimeouts(o =>
o.DefaultPolicy = new RequestTimeoutPolicy { Timeout = TimeSpan.FromSeconds(60) });
app.UseTCISCore(); // TExceptionMiddleware phải BỌC NGOÀI để phân loại được sự cố
app.UseRequestTimeouts();
TExceptionMiddleware phân biệt server hết hạn với client tự ngắt bằng
IHttpRequestTimeoutFeature.RequestTimeoutToken — cả hai đều biểu hiện bằng
OperationCanceledException nên không thể phân biệt bằng kiểu ngoại lệ.
| Tình huống | HTTP | Mức log |
|---|---|---|
| Request vượt ngưỡng thời gian của server | 504 GATEWAY_TIMEOUT |
Error → cảnh báo kích hoạt |
| Client đóng kết nối | 499 CLIENT_CLOSED |
Information → không cảnh báo |
Ranh giới này được ghim bằng RequestTimeoutClassificationTests. Phân loại nhầm không chỉ sai mã
HTTP mà còn sai mức log, khiến sự cố quá tải trôi qua mọi hệ thống giám sát.
📈 Số đo — cơ chế phát hiện sự cố duy nhất
Circuit breaker từng đảm nhiệm việc phát hiện một Step hỏng lặp lại. Nó đã bị gỡ bỏ vì bảo vệ sai mối đe doạ, nhưng tín hiệu thì vẫn cần. Từ nay cảnh báo là cơ chế cắt mạch duy nhất.
builder.Services.AddOpenTelemetry()
.WithMetrics(m => m.AddMeter(PipelineMetrics.MeterName)); // "TCIS.Platform.Pipelines"
| Số đo | Ý nghĩa |
|---|---|
tcis.pipeline.step.duration |
Thời gian thực thi Step (ms) |
tcis.pipeline.step.errors |
Step ném ngoại lệ — sự cố hạ tầng |
tcis.pipeline.step.business_failures |
Step dừng pipeline bằng context.Fail — nghiệp vụ |
Cả ba đều mang nhãn tcis.site_code, tcis.step_key, tcis.context.
Lỗi nghiệp vụ được đếm riêng. Từ chối một container quá tải trọng là hệ thống đang làm đúng việc của nó — trộn hai loại vào một số đo sẽ khiến mọi ngưỡng cảnh báo trở nên vô nghĩa.
Và nhãn tcis.site_code là bắt buộc chứ không phải trang trí: nhiều cảng nhỏ dùng chung một tiến
trình, nên tổng số lỗi toàn hệ thống che mất đúng tình huống cần cảnh báo nhất — một cảng hỏng
hoàn toàn trong khi chín cảng còn lại vẫn xanh.
Quy tắc cảnh báo gợi ý (PromQL):
# Một Step của một cảng hỏng liên tục — thay cho vai trò của circuit breaker
sum by (tcis_site_code, tcis_step_key) (rate(tcis_pipeline_step_errors_total[5m])) > 0.2
# Một Step chậm bất thường — dấu hiệu sớm của DB treo
histogram_quantile(0.95,
sum by (le, tcis_site_code, tcis_step_key) (rate(tcis_pipeline_step_duration_bucket[5m]))) > 5000
Hành vi lỗi của Engine
| Tình huống | Hành vi |
|---|---|
Step gọi context.Fail(...) |
Lỗi nghiệp vụ — ở lại trong Context, pipeline dừng (hoặc tiếp tục nếu ContinueOnError). |
| Step ném Exception | Lỗi hạ tầng — Engine ghi log rồi ném tiếp, giữ nguyên kiểu để TExceptionMiddleware map đúng HTTP status (503). Engine không hạ cấp thành lỗi nghiệp vụ. Các Step sau không chạy. |
| Không resolve được Step từ DI | PipelineDiscoveryException. Engine không fallback sang ActivatorUtilities — làm vậy sẽ mất toàn bộ AOP Logging/Telemetry mà không ai biết. |
Chỉ còn ba tình huống, và không tình huống nào bỏ qua một Step. Mỗi Step hoặc chạy, hoặc làm dừng pipeline — nên lớp lỗi "pipeline báo thành công dù Step nghiệp vụ chưa chạy" (phát hiện P1-8) nay không còn tồn tại về mặt cấu trúc.
💡 Usage
Automatically register services by suffix (Auto-MultiTenantService) or utilize Attributes:
using TCIS.Pluggable.Abstractions.Pipelines.Attributes;
using TCIS.Pluggable.Abstractions.Pipelines.Abstractions;
[PipelineStep(StepKey = "ValidateOrder", Priority = 1, ExecutionOrder = 10)]
public class ValidateOrderStep : IPipelineStep<OrderContext>
{
public Task ExecuteAsync(OrderContext context, CancellationToken cancellationToken)
{
// ... logic
return Task.CompletedTask;
}
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Options (>= 9.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.0)
- TCIS.Core (>= 1.0.0-rc.12)
- TCIS.Logging.Abstractions (>= 1.0.0-rc.12)
- TCIS.Pluggable.Abstractions (>= 1.0.0-rc.12)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on TCIS.Pluggable.Engine:
| Package | Downloads |
|---|---|
|
TCIS.Pluggable.Engine.Autofac
TCIS Core Framework is an application framework for building modular, multi-tenant applications on ASP.NET Core. Autofac integration for TCIS.Pluggable.Engine |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0-rc.33 | 20 | 8/21/2026 |
| 1.0.0-rc.32 | 24 | 8/21/2026 |
| 1.0.0-rc.31 | 31 | 8/21/2026 |
| 1.0.0-rc.30 | 28 | 8/21/2026 |
| 1.0.0-rc.29 | 25 | 8/21/2026 |
| 1.0.0-rc.28 | 29 | 8/21/2026 |
| 1.0.0-rc.27 | 28 | 8/21/2026 |
| 1.0.0-rc.26 | 41 | 8/20/2026 |
| 1.0.0-rc.25 | 41 | 8/20/2026 |
| 1.0.0-rc.24 | 43 | 8/20/2026 |
| 1.0.0-rc.23 | 39 | 8/20/2026 |
| 1.0.0-rc.22 | 45 | 8/19/2026 |
| 1.0.0-rc.21 | 45 | 8/19/2026 |
| 1.0.0-rc.20 | 59 | 8/18/2026 |
| 1.0.0-rc.19 | 72 | 8/13/2026 |
| 1.0.0-rc.18 | 57 | 8/13/2026 |
| 1.0.0-rc.17 | 54 | 8/13/2026 |
| 1.0.0-rc.16 | 62 | 8/13/2026 |
| 1.0.0-rc.15 | 60 | 8/12/2026 |
| 1.0.0-rc.12 | 58 | 8/10/2026 |