[Developer] #567 filter recent SQL cursors in Java
This commit is contained in:
@@ -45,6 +45,7 @@ public class OrdsProbeService {
|
||||
private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() {
|
||||
};
|
||||
private static final Logger log = LoggerFactory.getLogger(OrdsProbeService.class);
|
||||
private static final int EXECUTION_EVIDENCE_LOOKBACK_MINUTES = 15;
|
||||
|
||||
private record ExecutionEvidenceLookup(
|
||||
SqlExecutionEvidence evidence,
|
||||
@@ -52,6 +53,18 @@ public class OrdsProbeService {
|
||||
) {
|
||||
}
|
||||
|
||||
private record RecentSqlCursor(
|
||||
String sqlId,
|
||||
int childNumber,
|
||||
String sqlFulltext,
|
||||
String lastActiveAt,
|
||||
long executions,
|
||||
long rowsProcessed,
|
||||
long elapsedMillis,
|
||||
long bufferGets
|
||||
) {
|
||||
}
|
||||
|
||||
private final BearerTokenService tokenService;
|
||||
private final ProtectedObjectService protectedObjectService;
|
||||
private final AuditService auditService;
|
||||
@@ -260,8 +273,8 @@ public class OrdsProbeService {
|
||||
+ "V$SQL과 DBMS_XPLAN은 일반 데이터 접근과 별도인 SYS 진단 권한입니다.");
|
||||
}
|
||||
return result.withExecutionEvidence(null,
|
||||
"최근 2분 내 이 보호 대상의 실행 cursor를 찾지 못했습니다. "
|
||||
+ "해당 ORDS 요청이 DB까지 실행됐는지 확인하세요.");
|
||||
"최근 15분의 최신 실행 cursor 10건에서 이 보호 대상을 찾지 못했습니다. "
|
||||
+ "ORDS 응답 지연 또는 shared pool 교체로 증적이 남지 않았을 수 있습니다.");
|
||||
}
|
||||
String message = evidence.hasPredicatePlan() ? null
|
||||
: "SQL_ID는 찾았지만 DBMS_XPLAN Predicate Information을 읽지 못했습니다. "
|
||||
@@ -299,9 +312,7 @@ public class OrdsProbeService {
|
||||
JdbcTemplate evidenceJdbcTemplate,
|
||||
ProtectedObject object
|
||||
) {
|
||||
String marker = "%FROM%" + object.owner().toUpperCase(Locale.ROOT)
|
||||
+ "." + object.objectName().toUpperCase(Locale.ROOT) + "%";
|
||||
return evidenceJdbcTemplate.query("""
|
||||
List<RecentSqlCursor> candidates = evidenceJdbcTemplate.query("""
|
||||
SELECT sql_id,
|
||||
child_number,
|
||||
sql_fulltext,
|
||||
@@ -320,31 +331,49 @@ public class OrdsProbeService {
|
||||
elapsed_time,
|
||||
buffer_gets
|
||||
FROM v$sql
|
||||
WHERE UPPER(sql_text) LIKE ?
|
||||
AND last_active_time >= SYSTIMESTAMP - INTERVAL '2' MINUTE
|
||||
WHERE last_active_time >= SYSTIMESTAMP - NUMTODSINTERVAL(?, 'MINUTE')
|
||||
ORDER BY last_active_time DESC
|
||||
)
|
||||
WHERE ROWNUM = 1
|
||||
""", resultSet -> {
|
||||
if (!resultSet.next()) {
|
||||
return null;
|
||||
}
|
||||
String sqlId = resultSet.getString("sql_id");
|
||||
int childNumber = resultSet.getInt("child_number");
|
||||
String predicatePlan = findPredicatePlan(evidenceJdbcTemplate, sqlId, childNumber);
|
||||
return new SqlExecutionEvidence(
|
||||
sqlId,
|
||||
childNumber,
|
||||
resultSet.getString("sql_fulltext"),
|
||||
resultSet.getTimestamp("last_active_time") == null
|
||||
? null : resultSet.getTimestamp("last_active_time").toLocalDateTime().toString(),
|
||||
resultSet.getLong("executions"),
|
||||
resultSet.getLong("rows_processed"),
|
||||
Math.round(resultSet.getLong("elapsed_time") / 1000.0d),
|
||||
resultSet.getLong("buffer_gets"),
|
||||
predicatePlan
|
||||
);
|
||||
}, marker);
|
||||
WHERE ROWNUM <= 10
|
||||
""", (resultSet, rowNum) -> new RecentSqlCursor(
|
||||
resultSet.getString("sql_id"),
|
||||
resultSet.getInt("child_number"),
|
||||
resultSet.getString("sql_fulltext"),
|
||||
resultSet.getTimestamp("last_active_time") == null
|
||||
? null : resultSet.getTimestamp("last_active_time").toLocalDateTime().toString(),
|
||||
resultSet.getLong("executions"),
|
||||
resultSet.getLong("rows_processed"),
|
||||
Math.round(resultSet.getLong("elapsed_time") / 1000.0d),
|
||||
resultSet.getLong("buffer_gets")
|
||||
), EXECUTION_EVIDENCE_LOOKBACK_MINUTES);
|
||||
|
||||
return candidates.stream()
|
||||
.filter(candidate -> referencesProtectedObject(candidate.sqlFulltext(), object))
|
||||
.findFirst()
|
||||
.map(candidate -> new SqlExecutionEvidence(
|
||||
candidate.sqlId(),
|
||||
candidate.childNumber(),
|
||||
candidate.sqlFulltext(),
|
||||
candidate.lastActiveAt(),
|
||||
candidate.executions(),
|
||||
candidate.rowsProcessed(),
|
||||
candidate.elapsedMillis(),
|
||||
candidate.bufferGets(),
|
||||
findPredicatePlan(evidenceJdbcTemplate, candidate.sqlId(), candidate.childNumber())
|
||||
))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
static boolean referencesProtectedObject(String sqlFulltext, ProtectedObject object) {
|
||||
if (sqlFulltext == null || object == null) {
|
||||
return false;
|
||||
}
|
||||
String normalizedSql = sqlFulltext.toUpperCase(Locale.ROOT)
|
||||
.replace("\"", "")
|
||||
.replaceAll("\\s+", "");
|
||||
String objectName = (object.owner() + "." + object.objectName())
|
||||
.toUpperCase(Locale.ROOT);
|
||||
return normalizedSql.contains(objectName);
|
||||
}
|
||||
|
||||
private String findPredicatePlan(
|
||||
|
||||
Reference in New Issue
Block a user