Metadata、Platform Cache、Knowledge 与 Files
本章涵盖四大平台特性:Metadata 命名空间(检索/部署元数据——自定安装升级和安装后配置)、Platform Cache(会话/组织缓存提升性能——分布式缓存空间防占用)、Salesforce Knowledge(文章全生命周期管理——发布/归档/翻译/审阅)和 Salesforce Files(下载前定制——杀毒扫描/IRM 控制/设备限制)。
Metadata 操作:检索与部署
Metadata.Operations.retrieve() 同步检索当前组织元数据——提供组件名称列表,返回 Metadata.Metadata 派生类匹配组件数据。Metadata.Operations.enqueueDeployment() 异步部署到当前组织——可创建和更新组件但不能删除。需使用组件全名——格式:namespace__TypeName__mdt.namespace__ComponentName。更新命名空间中的组件时必须限定命名空间。
支持:自定义元数据类型记录和布局(Layout)。适用于 API 40.0+。安装后脚本中可检索和部署——卸载脚本中仅可检索。异步部署作业有服务保护限制——限制 Apex 发起的 Metadata API 部署同时入队数量。
典型用例:
- 应用安装/升级时自定义:安装或升级期间创建或更新元数据让用户配置你的应用
- 安装后自定义:通过自有 UI 让管理员配置应用(而非手动使用标准 Salesforce Setup UI)
- 安全访问受保护元数据:更新应用内部使用的元数据而不暴露这些类型和组件给用户
Metadata:安全与测试
安全注意事项:
- 安装的 Apex 类可访问订阅者组织中任何公开支持的元数据类型或组件
- 受保护元数据(如标记为 protected 的自定义元数据类型)仅同命名空间的 Apex 类可访问
- 非认证托管包访问公开或受保护元数据需启用 Deploy Metadata from Non-Certified Package Versions via Apex 组织首选项(Setup → Apex Settings)——用于安全审查前测试或试验目的
- 应用必须在包描述中通知用户可检索或部署元数据——Salesforce 安全审查期间验证此通知。示例声明:"This package can access and change metadata outside its namespace in the Salesforce org where it's installed."
部署限制:
enqueueDeployment()使用异步 Apex——部署作业和回调计入当前组织的异步作业限制- 所有标准 Apex Governor Limit 适用
- 限制 Apex 发起的 Metadata API 部署同时入队数量——保持服务功能
- 回调在异步上下文中运行——异步限制适用
测试 Metadata 部署:
- 部署请求代码测试:验证 DeployContainer 包含正确的元数据组件和组件值——断言需要部署的准确内容
- 部署结果代码测试:验证 DeployCallback 处理预期和非预期结果。创建
Metadata.DeployCallbackContext子类——重写getCallbackJobId()返回测试 ID。直接实例化你的回调类并构造测试 DeployResults 测试handleResults()方法
Permission Set Groups 与 Platform Cache 简介
Permission Set Groups:Test.calculatePermissionSetGroup(psgId) 强制立即计算聚合权限——计入 Apex CPU 限制。PSG 状态必须为 'Updated' 后才能分配。复杂的 PSG(大量包含权限集或总体启用权限多)+ 大量用户分配 → CPU 限制超出导致测试失败。最佳实践:在 @testSetup 方法中运行一次并在后续测试中复用数据。
Platform Cache 两种类型:
- Session Cache:存储单用户会话数据——例如浏览不同地图位置时重用计算结果。最长存活 8 小时或
ttlsecs到期(以先到者为准)。Session cache 随会话生命存在 - Org Cache:数据跨会话/请求/用户可访问——例如基于用户配置文件动态显示菜单项的内容。仅
ttlsecs到期——最长 48 小时
容量:Enterprise 10MB、Unlimited+Performance 30MB、其他 0MB。安全审查托管包额外 3MB Provider Free 容量(可分配给 session/org cache)。
核心缓存特性:
- 不持久化——无数据丢失保证(不用于关键数据存储)
- 修改 Apex 类使缓存失效——全部或部分缓存数据可能丢失
- 数据不加密——不存储敏感信息
- 事务失败时缓存操作回滚——每个缓存操作依赖运行中的 Apex 事务
- 最佳缓存候选:会话中重用 + 静态(不快速变化)+ 否则获取昂贵
- Flow 限制:包含 Pause 元素或 Scheduled Action 的 Flow 中——后续步骤不能访问 Session Cache
Platform Cache:特性与注意事项
存储/检索类:
- Cache.Session / Cache.Org:管理任意分区值——键格式
namespace.partition.key - Cache.SessionPartition / Cache.OrgPartition:管理单个分区值——更简洁(仅需键名)
命名空间可见性:Cache.Visibility 枚举——ALL(任何命名空间可见)或限制特定命名空间。local 前缀代表当前代码运行组织命名空间——托管包中指向订阅者组织(不是包命名空间)。
重要并发行为:Org cache 支持跨多事务并发读写——同 key 同时更新任选其一作为赢家。选择是 per-key 而非 per-transaction——例如同时一个事务写 PetType="Cat"+PetName="Felix",另一事务写 PetType="Dog"+PetName="Fido"——结果可能是 PetType="Cat" 且 PetName="Fido"。
缓存缺失处理:必须处理缓存返回 null 的情况——或使用 CacheBuilder 接口自动处理。
关键限制:
- Key 最大 50 字符
- 单缓存项最大 100KB——超出抛
Cache.ItemSizeLimitExceededException - Session local cache 500KB/request | Org local cache 1,000KB/request
- Session ttl 300s(5min) ~ 28,800s(8h) | Org ttl 300s ~ 172,800s(48h,默认 86,400s/24h)
- 统计方法(getAvgGetSize/getMaxGetSize/getMissRate 等)从缓存服务器重启后开始计——不包含重启前数据
Platform Cache:分区
Setup → Platform Cache → 使用 Partition 工具:创建/编辑/删除分区——分配 session 和 org 容量平衡应用性能。每个分区有一个 session cache 和一个 org cache 段——分别分配容量。
分区规则:
- 可定义一个默认分区——默认分区中操作省略分区名
- Session/org 分配可为 0 或 ≥5(整数)
- 所有分区分配总和 = Platform Cache 总分配 ≤ 组织总容量
- 打包:分区不自动包含在包中——需显式添加。运行时验证而非编译时——缺失分区不报编译错误
- 默认分区不能打包部署——使用显式非默认分区替代
- 查看当前容量/分配/分区详情快照
Session Cache:存储与检索
Cache.Session 方法——跨分区操作:
// 键格式:namespace.partition.key
Cache.Session.put('ns1.partition1.orderDate', dt);
if (Cache.Session.contains('ns1.partition1.orderDate')) {
DateTime cachedDt = (DateTime) Cache.Session.get('ns1.partition1.orderDate');
}
// 默认分区+调用类命名空间——省略前缀
Cache.Session.put('orderDate', dt);
// local 前缀 = 当前组织命名空间(托管包=订阅者组织)
Cache.Session.put('local.myPartition.orderDate', dt); // = ns1.myPartition.orderDate
// put 重载——设置 ttl + 可见性 + 不可覆盖
Cache.Session.put('ns1.partition1.totalSum', '500', 3600, Cache.Visibility.ALL, true);
Cache.SessionPartition——单分区操作:
Cache.SessionPartition part = Cache.Session.getPartition('myNs.myPartition');
part.put('OrderDate', Date.today());
String title = (String) part.get('BookTitle');
// 点符号一行调用
String author = (String) Cache.Session.getPartition('myNs.myPartition').get('BookAuthor');
Org Cache:存储与检索
与 Session Cache 完全相同的 API 模式——Cache.Org.put/get/contains/remove。键格式命名规则、local 前缀、put 重载方法一致。OrgPartition 等效于 SessionPartition。
Visualforce 全局变量:{!$Cache.Session.ns.part.key} | {!$Cache.Org.ns.part.key}——支持点符号访问属性和方法(List.size/自定义类属性)。不同于 Apex 方法——不可省略 namespace.partition 前缀。无命名空间组织用 local。CacheBuilder 类:键名加 _B_ 后缀和实现类名——如 {!$Cache.Session.ns.part.CacheBuilderImpl_B_key}。
CacheBuilder 接口:安全缓存
最佳实践——创建实现 Cache.CacheBuilder 的内部类(非 static——Salesforce 实例化新实例执行 doLoad),重写 doLoad(String var) 构建缓存值。Salesforce 自动:检查缓存→命中返回→未命中执行 doLoad→缓存结果→返回。唯一键 = 类名 + doLoad String 参数。
要求:doLoad 必须接受 String 参数(即使不使用——Salesforce 用于构建唯一键)。返回 null 时不缓存值——消费者需处理 null(建议用作临时重建失败信号)。不需写 put() 或 null 检查代码。
class UserInfoCache implements Cache.CacheBuilder {
public Object doLoad(String userid) {
return [SELECT Id, IsActive, username FROM User WHERE Id = :userid];
}
}
User batman = (User) Cache.Org.get(UserInfoCache.class, '00541000000ek4c');
Platform Cache:最佳实践与内部机制
性能评估:使用 System.currentTimeMillis() 而非 debug log 时间戳——计算实际耗时对比有无缓存。
最佳实践清单:
- 优雅处理缺失:CacheBuilder 或手动 null 检查。添加 debug 日志用于缓存操作追踪
- 分组请求:批量 get(keys) 而非逐个 get——在初始化方法中一次获取所有需要的键
- 缓存大项:缓存少量大项(如 Apex 类封装数据实例)比许多单独小项更高效——减少序列化大小/时间/提交时间/容量使用
- 避免昂贵操作:
Cache.Org.getKeys()/getCapacity()遍历所有分区信息——少用。不要contains(key)+get(key)连用——直接 get 并检查 null。非必要不清空缓存——清空遍历所有分区空间 + 应用重新生成缓存(数据库查询+计算) - 注意限制:100KB 单缓存项 → ItemSizeLimitExceededException 需捕获缩小。超出 partition limit → LRU 淘汰。超出 local cache limit → 请求内淘汰(导致意外缺失+浪费资源)
内部机制:Platform Cache 使用本地缓存(应用服务器内存容器)——首次请求加载 session cache 所有项;org cache get 操作从缓存层检索值存储到本地缓存。可变操作(put/remove)先对本地缓存执行——请求成功后提交。LRU 淘汰算法:达到限制时淘汰键至 100% 容量。Session cache 被淘汰时从所有现有 session cache 实例中均匀移除。
Salesforce Knowledge:文章管理
使用 KbManagement.PublishingService 类管理文章及翻译全生命周期:发布/更新/检索/删除/提交翻译/设置翻译完成或未完成状态/归档/分配草稿或翻译审阅任务。日期值基于 GMT。需启用 Salesforce Knowledge。
推广搜索词(Promoted Search Terms):SearchPromotionRule sObject——关联关键词到已发布文章(PublishStatus=Online)提升搜索排名。需启用 Knowledge 才能对 SearchPromotionRule 执行 DML。
文章建议:Search.suggest(searchText, objectType, options)——返回标题匹配搜索字符串的文章列表。通过 Search.KnowledgeSuggestionFilter 设置语言(language)和发布状态(Online/Draft)。支持分页交互(首次 5 条 + 更多结果按钮)。Search.SuggestionResults 含 hasMoreResults() 和 getSuggestionResults()。
Salesforce Files:下载定制
Sfc 命名空间(API 39.0+)定制文件下载行为——定制代码在下载前运行决定是否允许。实现 Sfc.ContentDownloadHandlerFactory 接口——工厂类返回 ContentDownloadHandler 处理器。
ContentDownloadHandler 属性:isDownloadAllowed(是否允许下载)、redirectUrl(不允许时重定向 URL——传递问题文件 ID 列表)。
支持场景:
- 基于用户配置文件/设备/文件类型和大小阻止下载
- 应用 IRM 控制——追踪文件下载次数、在 IRM 系统处理后提供下载端点
- 标记可疑文件——重定向到杀毒扫描
- 多文件下载和 Content Pack 下载——设置 isDownloadAllowed=false 导致整个下载失败。通过 redirectUrl 传递问题文件列表
执行流程:UI/Connect API/sObject 调用触发下载 → 查找 Sfc.ContentDownloadHandlerFactory 实现 → 如未找到则直接下载 → 如找到多个实现按名称排序级联处理——第一个不允许下载的实现被使用。SOAP API 下载:通过 Apex 类检查但不支持重定向——返回错误消息异常替代。
Metadata API 检索部署、Platform Cache 双层性能优化、Knowledge 文章全生命周期和 Files 下载安全定制——共同构成 Salesforce 平台深度定制和性能优化的核心工具集。