fix(commits): compute total count on cache miss for pagination
- git_commit_log now computes count when Redis cache misses - Previous: returned total: 0 when cache empty - Now: compute + cache on miss (5min TTL)
This commit is contained in:
parent
ddd24bfb6d
commit
21d0d1eae6
@ -818,6 +818,8 @@ impl AppService {
|
||||
"git:commit:count:{}:{}:{:?}:{:?}",
|
||||
namespace, repo_name, from, to,
|
||||
);
|
||||
|
||||
// Get total count for pagination. Cache miss → compute + cache.
|
||||
let total: usize = if let Ok(mut conn) = self.cache.conn().await {
|
||||
if let Ok(cached) = conn.get::<_, String>(total_cache_key.clone()).await {
|
||||
if let Ok(cached) = serde_json::from_str::<CommitCountResponse>(&cached) {
|
||||
@ -826,10 +828,28 @@ impl AppService {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
// Cache miss: compute count and cache it.
|
||||
let computed = git_spawn!(repo, domain -> {
|
||||
domain.commit_count(from.as_deref(), to.as_deref())
|
||||
}).unwrap_or(0);
|
||||
if let Err(e) = conn
|
||||
.set_ex::<String, String, ()>(
|
||||
total_cache_key.clone(),
|
||||
serde_json::to_string(&CommitCountResponse { count: computed })
|
||||
.unwrap_or_default(),
|
||||
300,
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::debug!(error = ?e, "cache set failed (non-fatal)");
|
||||
}
|
||||
computed
|
||||
}
|
||||
} else {
|
||||
0
|
||||
// No cache: compute directly.
|
||||
git_spawn!(repo, domain -> {
|
||||
domain.commit_count(from.as_deref(), to.as_deref())
|
||||
}).unwrap_or(0)
|
||||
};
|
||||
|
||||
let total_pages = if total == 0 {
|
||||
|
||||
@ -52,6 +52,8 @@ export const RepoCommits = () => {
|
||||
const [copiedSha, setCopiedSha] = useState<string | null>(null);
|
||||
const [showBranchDropdown, setShowBranchDropdown] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<"list" | "graph" | "reflog">("list");
|
||||
const PAGE_SIZE = 50;
|
||||
const [page, setPage] = useState(1);
|
||||
|
||||
const { data: branches = [] } = useQuery({
|
||||
queryKey: ["repo-branches", namespace, repoName],
|
||||
@ -156,6 +158,7 @@ export const RepoCommits = () => {
|
||||
// Reset to page 1 when search changes
|
||||
const handleSearchChange = (value: string) => {
|
||||
setSearchQuery(value);
|
||||
setPage(1);
|
||||
};
|
||||
|
||||
const handleCopySha = async (sha: string) => {
|
||||
@ -304,11 +307,11 @@ export const RepoCommits = () => {
|
||||
</div>
|
||||
) : (
|
||||
<div className="divide-y">
|
||||
{filteredCommits.slice(0, 100).map((commit, index) => {
|
||||
{filteredCommits.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE).map((commit, index) => {
|
||||
const { title, description } = getCommitMessageLines(commit.message);
|
||||
const isCopied = copiedSha === commit.oid;
|
||||
const isMergeCommit = commit.parent_ids?.length > 1;
|
||||
const isLast = index === Math.min(filteredCommits.length, 100) - 1;
|
||||
const isLast = index === Math.min(filteredCommits.length - (page - 1) * PAGE_SIZE, PAGE_SIZE) - 1;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -442,10 +445,36 @@ export const RepoCommits = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{filteredCommits.length > 100 && activeTab === "list" && (
|
||||
<p className="text-sm text-muted-foreground text-center py-2">
|
||||
Showing 100 of {filteredCommits.length} commits
|
||||
</p>
|
||||
{activeTab === "list" && filteredCommits.length > 0 && (
|
||||
<div className="flex items-center justify-center gap-2 py-4">
|
||||
<button
|
||||
onClick={() => setPage(p => Math.max(1, p - 1))}
|
||||
disabled={page <= 1}
|
||||
className="px-3 py-1.5 text-sm rounded-md border bg-background hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
{Array.from({ length: Math.ceil(filteredCommits.length / PAGE_SIZE) }, (_, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setPage(i + 1)}
|
||||
className={`px-3 py-1.5 text-sm rounded-md transition-colors ${
|
||||
page === i + 1
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "border bg-background hover:bg-muted"
|
||||
}`}
|
||||
>
|
||||
{i + 1}
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
onClick={() => setPage(p => Math.min(Math.ceil(filteredCommits.length / PAGE_SIZE), p + 1))}
|
||||
disabled={page >= Math.ceil(filteredCommits.length / PAGE_SIZE)}
|
||||
className="px-3 py-1.5 text-sm rounded-md border bg-background hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user