Fossil SCM
Merge the /artifact_size_stats page into the /artifact_stats page. Make that page accessible to anybody with check-in privilege.
Commit
cefadbd5ce4dba55d73f5819cd224476d685d992dadab910559b283455c03720
Parent
4439f15d6c033fc…
2 files changed
+4
+106
-93
+4
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -1023,10 +1023,12 @@ | ||
| 1023 | 1023 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1024 | 1024 | style_header("List Of Artifacts"); |
| 1025 | 1025 | style_submenu_element("250 Largest", "bigbloblist"); |
| 1026 | 1026 | if( g.perm.Admin ){ |
| 1027 | 1027 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1028 | + } | |
| 1029 | + if( g.perm.Write ){ | |
| 1028 | 1030 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1029 | 1031 | } |
| 1030 | 1032 | if( !unpubOnly && mx>n && P("s")==0 ){ |
| 1031 | 1033 | int i; |
| 1032 | 1034 | @ <p>Select a range of artifacts to view:</p> |
| @@ -1097,10 +1099,12 @@ | ||
| 1097 | 1099 | |
| 1098 | 1100 | login_check_credentials(); |
| 1099 | 1101 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1100 | 1102 | if( g.perm.Admin ){ |
| 1101 | 1103 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1104 | + } | |
| 1105 | + if( g.perm.Write ){ | |
| 1102 | 1106 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1103 | 1107 | } |
| 1104 | 1108 | style_submenu_element("All Artifacts", "bloblist"); |
| 1105 | 1109 | style_header("%d Largest Artifacts", n); |
| 1106 | 1110 | db_multi_exec( |
| 1107 | 1111 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -1023,10 +1023,12 @@ | |
| 1023 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1024 | style_header("List Of Artifacts"); |
| 1025 | style_submenu_element("250 Largest", "bigbloblist"); |
| 1026 | if( g.perm.Admin ){ |
| 1027 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1028 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1029 | } |
| 1030 | if( !unpubOnly && mx>n && P("s")==0 ){ |
| 1031 | int i; |
| 1032 | @ <p>Select a range of artifacts to view:</p> |
| @@ -1097,10 +1099,12 @@ | |
| 1097 | |
| 1098 | login_check_credentials(); |
| 1099 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1100 | if( g.perm.Admin ){ |
| 1101 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1102 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1103 | } |
| 1104 | style_submenu_element("All Artifacts", "bloblist"); |
| 1105 | style_header("%d Largest Artifacts", n); |
| 1106 | db_multi_exec( |
| 1107 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -1023,10 +1023,12 @@ | |
| 1023 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1024 | style_header("List Of Artifacts"); |
| 1025 | style_submenu_element("250 Largest", "bigbloblist"); |
| 1026 | if( g.perm.Admin ){ |
| 1027 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1028 | } |
| 1029 | if( g.perm.Write ){ |
| 1030 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1031 | } |
| 1032 | if( !unpubOnly && mx>n && P("s")==0 ){ |
| 1033 | int i; |
| 1034 | @ <p>Select a range of artifacts to view:</p> |
| @@ -1097,10 +1099,12 @@ | |
| 1099 | |
| 1100 | login_check_credentials(); |
| 1101 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1102 | if( g.perm.Admin ){ |
| 1103 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1104 | } |
| 1105 | if( g.perm.Write ){ |
| 1106 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1107 | } |
| 1108 | style_submenu_element("All Artifacts", "bloblist"); |
| 1109 | style_header("%d Largest Artifacts", n); |
| 1110 | db_multi_exec( |
| 1111 |
+106
-93
| --- src/stat.c | ||
| +++ src/stat.c | ||
| @@ -95,11 +95,11 @@ | ||
| 95 | 95 | if( !brief ){ |
| 96 | 96 | @ <tr><th>Number Of Artifacts:</th><td> |
| 97 | 97 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 98 | 98 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 99 | 99 | @ %d(n) (%d(n-m) fulltext and %d(m) deltas) |
| 100 | - if( g.perm.Admin ){ | |
| 100 | + if( g.perm.Write ){ | |
| 101 | 101 | @ <a href='%R/artifact_stats'>Details</a> |
| 102 | 102 | } |
| 103 | 103 | @ </td></tr> |
| 104 | 104 | if( n>0 ){ |
| 105 | 105 | int a, b; |
| @@ -575,10 +575,12 @@ | ||
| 575 | 575 | style_footer(); |
| 576 | 576 | } |
| 577 | 577 | |
| 578 | 578 | /* |
| 579 | 579 | ** Gather statistics on artifact types, counts, and sizes. |
| 580 | +** | |
| 581 | +** Only populate the artstat.atype field if the bWithTypes parameter is true. | |
| 580 | 582 | */ |
| 581 | 583 | static void gather_artifact_stats(int bWithTypes){ |
| 582 | 584 | static const char zSql[] = |
| 583 | 585 | @ CREATE TEMP TABLE artstat( |
| 584 | 586 | @ id INTEGER PRIMARY KEY, -- Corresponds to BLOB.RID |
| @@ -637,66 +639,107 @@ | ||
| 637 | 639 | db_multi_exec("%s", zSql2/*safe-for-%s*/); |
| 638 | 640 | } |
| 639 | 641 | } |
| 640 | 642 | |
| 641 | 643 | /* |
| 642 | -** WEBPAGE: artifact_size_stats | |
| 644 | +** WEBPAGE: artifact_stats | |
| 643 | 645 | ** |
| 644 | -** Show information about the sizes of artifacts. | |
| 646 | +** Show information about the sizes of artifacts in this repository | |
| 645 | 647 | */ |
| 646 | -void artifact_size_stats_page(void){ | |
| 648 | +void artifact_stats_page(void){ | |
| 647 | 649 | Stmt q; |
| 648 | - int nTotal = 0; | |
| 649 | - int nDelta = 0; | |
| 650 | - int nFull = 0; | |
| 650 | + int nTotal = 0; /* Total number of artifacts */ | |
| 651 | + int nDelta = 0; /* Total number of deltas */ | |
| 652 | + int nFull = 0; /* Total number of full-texts */ | |
| 653 | + double avgCmpr = 0.0; /* Average size of an artifact after compression */ | |
| 654 | + double avgExp = 0.0; /* Average size of an uncompressed artifact */ | |
| 655 | + int mxCmpr = 0; /* Maximum compressed artifact size */ | |
| 656 | + int mxExp = 0; /* Maximum uncompressed artifact size */ | |
| 657 | + sqlite3_int64 sumCmpr = 0; /* Total size of all compressed artifacts */ | |
| 658 | + sqlite3_int64 sumExp = 0; /* Total size of all expanded artifacts */ | |
| 659 | + sqlite3_int64 sz1pct = 0; /* Space used by largest 1% */ | |
| 660 | + sqlite3_int64 sz10pct = 0; /* Space used by largest 10% */ | |
| 661 | + sqlite3_int64 sz25pct = 0; /* Space used by largest 25% */ | |
| 662 | + sqlite3_int64 sz50pct = 0; /* Space used by largest 50% */ | |
| 663 | + int n50pct = 0; /* Artifacts using the first 50% of space */ | |
| 664 | + int n; /* Loop counter */ | |
| 665 | + int medCmpr = 0; /* Median compressed artifact size */ | |
| 666 | + int medExp = 0; /* Median expanded artifact size */ | |
| 651 | 667 | int med; |
| 652 | 668 | double r; |
| 669 | + | |
| 653 | 670 | login_check_credentials(); |
| 654 | - if( !g.perm.Read ){ login_needed(g.anon.Read); return; } | |
| 655 | - style_header("Artifact Sizes"); | |
| 656 | - gather_artifact_stats(0); | |
| 657 | - @ <table class="label-value"> | |
| 671 | + | |
| 672 | + /* These stats are expensive to compute. To disable them for | |
| 673 | + ** user without check-in privileges, to prevent excessive usage by | |
| 674 | + ** robots and random passers-by on the internet | |
| 675 | + */ | |
| 676 | + if( !g.perm.Write ){ | |
| 677 | + login_needed(g.anon.Admin); | |
| 678 | + return; | |
| 679 | + } | |
| 680 | + | |
| 681 | + style_header("Artifact Statistics"); | |
| 682 | + style_submenu_element("Repository Stats", "stat"); | |
| 683 | + style_submenu_element("Artifact List", "bloblist"); | |
| 684 | + gather_artifact_stats(1); | |
| 685 | + | |
| 658 | 686 | db_prepare(&q, |
| 659 | - "SELECT count(*), sum(isDelta), avg(szCmpr), avg(szExp), max(szCmpr)," | |
| 660 | - " max(szExp)" | |
| 687 | + "SELECT count(*), sum(isDelta), max(szCmpr)," | |
| 688 | + " max(szExp), sum(szCmpr), sum(szExp)" | |
| 661 | 689 | " FROM artstat" |
| 662 | 690 | ); |
| 663 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 664 | - nTotal = db_column_int(&q,0); | |
| 665 | - nDelta = db_column_int(&q,1); | |
| 666 | - nFull = nTotal - nDelta; | |
| 667 | - @ <tr><th>Number of artifacts:</th><td>%d(nTotal)</td></tr> | |
| 668 | - if( nTotal>0 ){ | |
| 669 | - @ <tr><th>Number of deltas:</th>\ | |
| 670 | - @ <td>%d(nDelta) (%d(nDelta*100/nTotal)%%)</td></tr> | |
| 671 | - | |
| 672 | - @ <tr><th>Number of full-text:</th><td>%d(nFull) \ | |
| 673 | - @ (%d(nFull*100/nTotal)%%)</td></tr> | |
| 674 | - } | |
| 675 | - @ <tr><th>Largest compressed artifact size:</th>\ | |
| 676 | - @ <td>%d(db_column_int(&q,4))</td></tr> | |
| 677 | - | |
| 678 | - @ <tr><th>Average compressed artifact size:</th> \ | |
| 679 | - @ <td>%.2f(db_column_double(&q,2))</td></tr> | |
| 680 | - | |
| 681 | - db_multi_exec("CREATE INDEX artstatx1 ON artstat(szCmpr, isDelta);"); | |
| 682 | - med = db_int(0, "SELECT szCmpr FROM artstat ORDER BY szCmpr" | |
| 683 | - " LIMIT 1 OFFSET %d", nTotal/2); | |
| 684 | - @ <tr><th>Median compressed artifact size:</th><td>%d(med)</td></tr> | |
| 685 | - | |
| 686 | - @ <tr><th>Largest uncompressed artifact size:</td>\ | |
| 687 | - @ <td>%d(db_column_int(&q,5))</td></tr> | |
| 688 | - | |
| 689 | - @ <tr><th>Average uncompressed artifact size:</th> \ | |
| 690 | - @ <td>%.2f(db_column_double(&q,3))</td></tr> | |
| 691 | - | |
| 692 | - med = db_int(0, "SELECT szExp FROM artstat ORDER BY szExp" | |
| 693 | - " LIMIT 1 OFFSET %d", nTotal/2); | |
| 694 | - @ <tr><th>Median uncompressed artifact size:</th><td>%d(med)</td></tr> | |
| 695 | - | |
| 691 | + db_step(&q); | |
| 692 | + nTotal = db_column_int(&q,0); | |
| 693 | + nDelta = db_column_int(&q,1); | |
| 694 | + nFull = nTotal - nDelta; | |
| 695 | + mxCmpr = db_column_int(&q, 2); | |
| 696 | + mxExp = db_column_int(&q, 3); | |
| 697 | + sumCmpr = db_column_int64(&q, 4); | |
| 698 | + sumExp = db_column_int64(&q, 5); | |
| 699 | + db_finalize(&q); | |
| 700 | + if( nTotal==0 ){ | |
| 701 | + @ No artifacts in this repository! | |
| 702 | + style_footer(); | |
| 703 | + return; | |
| 704 | + } | |
| 705 | + avgCmpr = (double)sumCmpr/nTotal; | |
| 706 | + avgExp = (double)sumExp/nTotal; | |
| 707 | + | |
| 708 | + db_prepare(&q, "SELECT szCmpr FROM artstat ORDER BY 1 DESC"); | |
| 709 | + r = 0; | |
| 710 | + n = 0; | |
| 711 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 712 | + r += db_column_int(&q, 0); | |
| 713 | + if( n50pct==0 && r>=sumCmpr/2 ) n50pct = n; | |
| 714 | + if( n==(nTotal+99)/100 ) sz1pct = r; | |
| 715 | + if( n==(nTotal+9)/10 ) sz10pct = r; | |
| 716 | + if( n==(nTotal+4)/5 ) sz25pct = r; | |
| 717 | + if( n==(nTotal+1)/2 ){ sz50pct = r; medCmpr = db_column_int(&q,0); } | |
| 718 | + n++; | |
| 696 | 719 | } |
| 697 | 720 | db_finalize(&q); |
| 721 | + | |
| 722 | + @ <h1>Overall Artifact Size Statistics:</h1> | |
| 723 | + @ <table class="label-value"> | |
| 724 | + @ <tr><th>Number of artifacts:</th><td>%d(nTotal)</td></tr> | |
| 725 | + @ <tr><th>Number of deltas:</th>\ | |
| 726 | + @ <td>%d(nDelta) (%d(nDelta*100/nTotal)%%)</td></tr> | |
| 727 | + @ <tr><th>Number of full-text:</th><td>%d(nFull) \ | |
| 728 | + @ (%d(nFull*100/nTotal)%%)</td></tr> | |
| 729 | + @ <tr><th>Largest compressed artifact size:</th>\ | |
| 730 | + @ <td>%d(mxCmpr)</td></tr> | |
| 731 | + @ <tr><th>Average compressed artifact size:</th> \ | |
| 732 | + @ <td>%.2f(avgCmpr)</td></tr> | |
| 733 | + @ <tr><th>Median compressed artifact size:</th><td>%d(medCmpr)</td></tr> | |
| 734 | + @ <tr><th>Largest uncompressed artifact size:</td>\ | |
| 735 | + @ <td>%d(mxExp)</td></tr> | |
| 736 | + @ <tr><th>Average uncompressed artifact size:</th> \ | |
| 737 | + @ <td>%.2f(avgExp)</td></tr> | |
| 738 | + medExp = db_int(0, "SELECT szExp FROM artstat ORDER BY szExp" | |
| 739 | + " LIMIT 1 OFFSET %d", nTotal/2); | |
| 740 | + @ <tr><th>Median uncompressed artifact size:</th><td>%d(medExp)</td></tr> | |
| 698 | 741 | db_prepare(&q, |
| 699 | 742 | "SELECT avg(szCmpr), max(szCmpr) FROM artstat WHERE isDelta" |
| 700 | 743 | ); |
| 701 | 744 | if( db_step(&q)==SQLITE_ROW ){ |
| 702 | 745 | @ <tr><th>Largest delta:</td>\ |
| @@ -708,62 +751,32 @@ | ||
| 708 | 751 | med = db_int(0, "SELECT szCmpr FROM artstat WHERE isDelta ORDER BY szCmpr" |
| 709 | 752 | " LIMIT 1 OFFSET %d", nDelta/2); |
| 710 | 753 | @ <tr><th>Median delta:</th><td>%d(med)</td></tr> |
| 711 | 754 | } |
| 712 | 755 | db_finalize(&q); |
| 713 | - | |
| 714 | 756 | r = db_double(0.0, "SELECT avg(szCmpr) FROM artstat WHERE NOT isDelta;"); |
| 715 | 757 | @ <tr><th>Average full-text artifact:</th><td>%.2f(r)</td></tr> |
| 716 | - | |
| 717 | 758 | med = db_int(0, "SELECT szCmpr FROM artstat WHERE NOT isDelta ORDER BY szCmpr" |
| 718 | 759 | " LIMIT 1 OFFSET %d", nFull/2); |
| 719 | 760 | @ <tr><th>Median full-text artifact:</th><td>%d(med)</td></tr> |
| 720 | 761 | @ </table> |
| 721 | - if( nTotal>0 ){ | |
| 722 | - sqlite3_int64 szTotal; | |
| 723 | - sqlite3_int64 szPart; | |
| 724 | - @ <h2>Artifact size distribution facts:</h2> | |
| 725 | - @ <ol> | |
| 726 | - szTotal = db_int64(0, "SELECT sum(szCmpr) FROM artstat"); | |
| 727 | - szPart = db_int64(0, | |
| 728 | - "SELECT sum(x) FROM (SELECT szCmpr AS x FROM artstat ORDER BY 1 DESC" | |
| 729 | - " LIMIT %d)", (nTotal+99)/100); | |
| 730 | - @ <li><p>The largest 1%% of artifacts (the largest %d((nTotal+99)/100) \ | |
| 731 | - @ artifacts) use %lld(szPart*100/szTotal)%% of the total artifact space. | |
| 732 | - szPart = db_int64(0, | |
| 733 | - "SELECT sum(x) FROM (SELECT szCmpr AS x FROM artstat ORDER BY 1 DESC" | |
| 734 | - " LIMIT %d)", (nTotal+9)/10); | |
| 735 | - @ <li><p>The largest 10%% of artifacts (the largest %d((nTotal+9)/10) \ | |
| 736 | - @ artifacts) use %lld(szPart*100/szTotal)%% of the total artifact space. | |
| 737 | - szPart = db_int64(0, | |
| 738 | - "SELECT sum(x) FROM (SELECT szCmpr AS x FROM artstat ORDER BY 1 DESC" | |
| 739 | - " LIMIT %d)", nTotal/4); | |
| 740 | - @ <li><p>The largest 25%% of artifacts (the largest %d(nTotal/4) \ | |
| 741 | - @ artifacts) use %lld(szPart*100/szTotal)%% of the total artifact space. | |
| 742 | - szPart = db_int64(0, | |
| 743 | - "SELECT sum(x) FROM (SELECT szCmpr AS x FROM artstat ORDER BY 1 DESC" | |
| 744 | - " LIMIT %d)", nTotal/2); | |
| 745 | - @ <li><p>The largest 50%% of artifacts (the largest %d(nTotal/2) \ | |
| 746 | - @ artifacts) use %lld(szPart*100/szTotal)%% of the total artifact space. | |
| 747 | - @ </ol> | |
| 748 | - } | |
| 749 | - style_footer(); | |
| 750 | -} | |
| 751 | - | |
| 752 | -/* | |
| 753 | -** WEBPAGE: artifact_stats | |
| 754 | -** | |
| 755 | -** Show information about the sizes of artifacts in this repository | |
| 756 | -*/ | |
| 757 | -void artifact_stats_page(void){ | |
| 758 | - Stmt q; | |
| 759 | - login_check_credentials(); | |
| 760 | - if( !g.perm.Admin ){ login_needed(g.anon.Admin); return; } | |
| 761 | - style_header("Artifact Statistics"); | |
| 762 | - style_submenu_element("Repository Stats", "stat"); | |
| 763 | - style_submenu_element("Artifact List", "bloblist"); | |
| 764 | - gather_artifact_stats(1); | |
| 762 | + | |
| 763 | + @ <h1>Artifact size distribution facts:</h1> | |
| 764 | + @ <ol> | |
| 765 | + @ <li><p>The largest 1%% of artifacts (the largest %d((nTotal+99)/100) \ | |
| 766 | + @ artifacts) use %lld(sz1pct*100/sumCmpr)%% of the total artifact space. | |
| 767 | + @ <li><p>The largest 10%% of artifacts (the largest %d((nTotal+9)/10) \ | |
| 768 | + @ artifacts) use %lld(sz10pct*100/sumCmpr)%% of the total artifact space. | |
| 769 | + @ <li><p>The largest 25%% of artifacts (the largest %d(nTotal/4) \ | |
| 770 | + @ artifacts) use %lld(sz25pct*100/sumCmpr)%% of the total artifact space. | |
| 771 | + @ <li><p>The largest 50%% of artifacts (the largest %d(nTotal/2) \ | |
| 772 | + @ artifacts) use %lld(sz50pct*100/sumCmpr)%% of the total artifact space. | |
| 773 | + @ <li><p>Half of the total artifact space is used by the %d(n50pct) \ | |
| 774 | + @ (%.1f(n50pct*100.0/nTotal)%%) largest artifacts. | |
| 775 | + @ </ol> | |
| 776 | + | |
| 777 | + @ <h1>Artifact Sizes By Type:</h1> | |
| 765 | 778 | db_prepare(&q, |
| 766 | 779 | "SELECT atype, count(*), sum(isDelta), sum(szCmpr), sum(szExp)" |
| 767 | 780 | " FROM artstat GROUP BY 1" |
| 768 | 781 | " UNION ALL " |
| 769 | 782 | "SELECT 'ALL', count(*), sum(isDelta), sum(szCmpr), sum(szExp)" |
| @@ -812,11 +825,11 @@ | ||
| 812 | 825 | } |
| 813 | 826 | @ </tbody></table> |
| 814 | 827 | db_finalize(&q); |
| 815 | 828 | |
| 816 | 829 | if( db_exists("SELECT 1 FROM artstat WHERE atype='unknown'") ){ |
| 817 | - @ <h2>Unknown Artifacts</h2> | |
| 830 | + @ <h1>Unknown Artifacts:</h1> | |
| 818 | 831 | db_prepare(&q, |
| 819 | 832 | "SELECT artstat.id, blob.uuid, user.login," |
| 820 | 833 | " datetime(rcvfrom.mtime), rcvfrom.rcvid" |
| 821 | 834 | " FROM artstat JOIN blob ON artstat.id=blob.rid" |
| 822 | 835 | " LEFT JOIN rcvfrom USING(rcvid)" |
| 823 | 836 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -95,11 +95,11 @@ | |
| 95 | if( !brief ){ |
| 96 | @ <tr><th>Number Of Artifacts:</th><td> |
| 97 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 98 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 99 | @ %d(n) (%d(n-m) fulltext and %d(m) deltas) |
| 100 | if( g.perm.Admin ){ |
| 101 | @ <a href='%R/artifact_stats'>Details</a> |
| 102 | } |
| 103 | @ </td></tr> |
| 104 | if( n>0 ){ |
| 105 | int a, b; |
| @@ -575,10 +575,12 @@ | |
| 575 | style_footer(); |
| 576 | } |
| 577 | |
| 578 | /* |
| 579 | ** Gather statistics on artifact types, counts, and sizes. |
| 580 | */ |
| 581 | static void gather_artifact_stats(int bWithTypes){ |
| 582 | static const char zSql[] = |
| 583 | @ CREATE TEMP TABLE artstat( |
| 584 | @ id INTEGER PRIMARY KEY, -- Corresponds to BLOB.RID |
| @@ -637,66 +639,107 @@ | |
| 637 | db_multi_exec("%s", zSql2/*safe-for-%s*/); |
| 638 | } |
| 639 | } |
| 640 | |
| 641 | /* |
| 642 | ** WEBPAGE: artifact_size_stats |
| 643 | ** |
| 644 | ** Show information about the sizes of artifacts. |
| 645 | */ |
| 646 | void artifact_size_stats_page(void){ |
| 647 | Stmt q; |
| 648 | int nTotal = 0; |
| 649 | int nDelta = 0; |
| 650 | int nFull = 0; |
| 651 | int med; |
| 652 | double r; |
| 653 | login_check_credentials(); |
| 654 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 655 | style_header("Artifact Sizes"); |
| 656 | gather_artifact_stats(0); |
| 657 | @ <table class="label-value"> |
| 658 | db_prepare(&q, |
| 659 | "SELECT count(*), sum(isDelta), avg(szCmpr), avg(szExp), max(szCmpr)," |
| 660 | " max(szExp)" |
| 661 | " FROM artstat" |
| 662 | ); |
| 663 | if( db_step(&q)==SQLITE_ROW ){ |
| 664 | nTotal = db_column_int(&q,0); |
| 665 | nDelta = db_column_int(&q,1); |
| 666 | nFull = nTotal - nDelta; |
| 667 | @ <tr><th>Number of artifacts:</th><td>%d(nTotal)</td></tr> |
| 668 | if( nTotal>0 ){ |
| 669 | @ <tr><th>Number of deltas:</th>\ |
| 670 | @ <td>%d(nDelta) (%d(nDelta*100/nTotal)%%)</td></tr> |
| 671 | |
| 672 | @ <tr><th>Number of full-text:</th><td>%d(nFull) \ |
| 673 | @ (%d(nFull*100/nTotal)%%)</td></tr> |
| 674 | } |
| 675 | @ <tr><th>Largest compressed artifact size:</th>\ |
| 676 | @ <td>%d(db_column_int(&q,4))</td></tr> |
| 677 | |
| 678 | @ <tr><th>Average compressed artifact size:</th> \ |
| 679 | @ <td>%.2f(db_column_double(&q,2))</td></tr> |
| 680 | |
| 681 | db_multi_exec("CREATE INDEX artstatx1 ON artstat(szCmpr, isDelta);"); |
| 682 | med = db_int(0, "SELECT szCmpr FROM artstat ORDER BY szCmpr" |
| 683 | " LIMIT 1 OFFSET %d", nTotal/2); |
| 684 | @ <tr><th>Median compressed artifact size:</th><td>%d(med)</td></tr> |
| 685 | |
| 686 | @ <tr><th>Largest uncompressed artifact size:</td>\ |
| 687 | @ <td>%d(db_column_int(&q,5))</td></tr> |
| 688 | |
| 689 | @ <tr><th>Average uncompressed artifact size:</th> \ |
| 690 | @ <td>%.2f(db_column_double(&q,3))</td></tr> |
| 691 | |
| 692 | med = db_int(0, "SELECT szExp FROM artstat ORDER BY szExp" |
| 693 | " LIMIT 1 OFFSET %d", nTotal/2); |
| 694 | @ <tr><th>Median uncompressed artifact size:</th><td>%d(med)</td></tr> |
| 695 | |
| 696 | } |
| 697 | db_finalize(&q); |
| 698 | db_prepare(&q, |
| 699 | "SELECT avg(szCmpr), max(szCmpr) FROM artstat WHERE isDelta" |
| 700 | ); |
| 701 | if( db_step(&q)==SQLITE_ROW ){ |
| 702 | @ <tr><th>Largest delta:</td>\ |
| @@ -708,62 +751,32 @@ | |
| 708 | med = db_int(0, "SELECT szCmpr FROM artstat WHERE isDelta ORDER BY szCmpr" |
| 709 | " LIMIT 1 OFFSET %d", nDelta/2); |
| 710 | @ <tr><th>Median delta:</th><td>%d(med)</td></tr> |
| 711 | } |
| 712 | db_finalize(&q); |
| 713 | |
| 714 | r = db_double(0.0, "SELECT avg(szCmpr) FROM artstat WHERE NOT isDelta;"); |
| 715 | @ <tr><th>Average full-text artifact:</th><td>%.2f(r)</td></tr> |
| 716 | |
| 717 | med = db_int(0, "SELECT szCmpr FROM artstat WHERE NOT isDelta ORDER BY szCmpr" |
| 718 | " LIMIT 1 OFFSET %d", nFull/2); |
| 719 | @ <tr><th>Median full-text artifact:</th><td>%d(med)</td></tr> |
| 720 | @ </table> |
| 721 | if( nTotal>0 ){ |
| 722 | sqlite3_int64 szTotal; |
| 723 | sqlite3_int64 szPart; |
| 724 | @ <h2>Artifact size distribution facts:</h2> |
| 725 | @ <ol> |
| 726 | szTotal = db_int64(0, "SELECT sum(szCmpr) FROM artstat"); |
| 727 | szPart = db_int64(0, |
| 728 | "SELECT sum(x) FROM (SELECT szCmpr AS x FROM artstat ORDER BY 1 DESC" |
| 729 | " LIMIT %d)", (nTotal+99)/100); |
| 730 | @ <li><p>The largest 1%% of artifacts (the largest %d((nTotal+99)/100) \ |
| 731 | @ artifacts) use %lld(szPart*100/szTotal)%% of the total artifact space. |
| 732 | szPart = db_int64(0, |
| 733 | "SELECT sum(x) FROM (SELECT szCmpr AS x FROM artstat ORDER BY 1 DESC" |
| 734 | " LIMIT %d)", (nTotal+9)/10); |
| 735 | @ <li><p>The largest 10%% of artifacts (the largest %d((nTotal+9)/10) \ |
| 736 | @ artifacts) use %lld(szPart*100/szTotal)%% of the total artifact space. |
| 737 | szPart = db_int64(0, |
| 738 | "SELECT sum(x) FROM (SELECT szCmpr AS x FROM artstat ORDER BY 1 DESC" |
| 739 | " LIMIT %d)", nTotal/4); |
| 740 | @ <li><p>The largest 25%% of artifacts (the largest %d(nTotal/4) \ |
| 741 | @ artifacts) use %lld(szPart*100/szTotal)%% of the total artifact space. |
| 742 | szPart = db_int64(0, |
| 743 | "SELECT sum(x) FROM (SELECT szCmpr AS x FROM artstat ORDER BY 1 DESC" |
| 744 | " LIMIT %d)", nTotal/2); |
| 745 | @ <li><p>The largest 50%% of artifacts (the largest %d(nTotal/2) \ |
| 746 | @ artifacts) use %lld(szPart*100/szTotal)%% of the total artifact space. |
| 747 | @ </ol> |
| 748 | } |
| 749 | style_footer(); |
| 750 | } |
| 751 | |
| 752 | /* |
| 753 | ** WEBPAGE: artifact_stats |
| 754 | ** |
| 755 | ** Show information about the sizes of artifacts in this repository |
| 756 | */ |
| 757 | void artifact_stats_page(void){ |
| 758 | Stmt q; |
| 759 | login_check_credentials(); |
| 760 | if( !g.perm.Admin ){ login_needed(g.anon.Admin); return; } |
| 761 | style_header("Artifact Statistics"); |
| 762 | style_submenu_element("Repository Stats", "stat"); |
| 763 | style_submenu_element("Artifact List", "bloblist"); |
| 764 | gather_artifact_stats(1); |
| 765 | db_prepare(&q, |
| 766 | "SELECT atype, count(*), sum(isDelta), sum(szCmpr), sum(szExp)" |
| 767 | " FROM artstat GROUP BY 1" |
| 768 | " UNION ALL " |
| 769 | "SELECT 'ALL', count(*), sum(isDelta), sum(szCmpr), sum(szExp)" |
| @@ -812,11 +825,11 @@ | |
| 812 | } |
| 813 | @ </tbody></table> |
| 814 | db_finalize(&q); |
| 815 | |
| 816 | if( db_exists("SELECT 1 FROM artstat WHERE atype='unknown'") ){ |
| 817 | @ <h2>Unknown Artifacts</h2> |
| 818 | db_prepare(&q, |
| 819 | "SELECT artstat.id, blob.uuid, user.login," |
| 820 | " datetime(rcvfrom.mtime), rcvfrom.rcvid" |
| 821 | " FROM artstat JOIN blob ON artstat.id=blob.rid" |
| 822 | " LEFT JOIN rcvfrom USING(rcvid)" |
| 823 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -95,11 +95,11 @@ | |
| 95 | if( !brief ){ |
| 96 | @ <tr><th>Number Of Artifacts:</th><td> |
| 97 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 98 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 99 | @ %d(n) (%d(n-m) fulltext and %d(m) deltas) |
| 100 | if( g.perm.Write ){ |
| 101 | @ <a href='%R/artifact_stats'>Details</a> |
| 102 | } |
| 103 | @ </td></tr> |
| 104 | if( n>0 ){ |
| 105 | int a, b; |
| @@ -575,10 +575,12 @@ | |
| 575 | style_footer(); |
| 576 | } |
| 577 | |
| 578 | /* |
| 579 | ** Gather statistics on artifact types, counts, and sizes. |
| 580 | ** |
| 581 | ** Only populate the artstat.atype field if the bWithTypes parameter is true. |
| 582 | */ |
| 583 | static void gather_artifact_stats(int bWithTypes){ |
| 584 | static const char zSql[] = |
| 585 | @ CREATE TEMP TABLE artstat( |
| 586 | @ id INTEGER PRIMARY KEY, -- Corresponds to BLOB.RID |
| @@ -637,66 +639,107 @@ | |
| 639 | db_multi_exec("%s", zSql2/*safe-for-%s*/); |
| 640 | } |
| 641 | } |
| 642 | |
| 643 | /* |
| 644 | ** WEBPAGE: artifact_stats |
| 645 | ** |
| 646 | ** Show information about the sizes of artifacts in this repository |
| 647 | */ |
| 648 | void artifact_stats_page(void){ |
| 649 | Stmt q; |
| 650 | int nTotal = 0; /* Total number of artifacts */ |
| 651 | int nDelta = 0; /* Total number of deltas */ |
| 652 | int nFull = 0; /* Total number of full-texts */ |
| 653 | double avgCmpr = 0.0; /* Average size of an artifact after compression */ |
| 654 | double avgExp = 0.0; /* Average size of an uncompressed artifact */ |
| 655 | int mxCmpr = 0; /* Maximum compressed artifact size */ |
| 656 | int mxExp = 0; /* Maximum uncompressed artifact size */ |
| 657 | sqlite3_int64 sumCmpr = 0; /* Total size of all compressed artifacts */ |
| 658 | sqlite3_int64 sumExp = 0; /* Total size of all expanded artifacts */ |
| 659 | sqlite3_int64 sz1pct = 0; /* Space used by largest 1% */ |
| 660 | sqlite3_int64 sz10pct = 0; /* Space used by largest 10% */ |
| 661 | sqlite3_int64 sz25pct = 0; /* Space used by largest 25% */ |
| 662 | sqlite3_int64 sz50pct = 0; /* Space used by largest 50% */ |
| 663 | int n50pct = 0; /* Artifacts using the first 50% of space */ |
| 664 | int n; /* Loop counter */ |
| 665 | int medCmpr = 0; /* Median compressed artifact size */ |
| 666 | int medExp = 0; /* Median expanded artifact size */ |
| 667 | int med; |
| 668 | double r; |
| 669 | |
| 670 | login_check_credentials(); |
| 671 | |
| 672 | /* These stats are expensive to compute. To disable them for |
| 673 | ** user without check-in privileges, to prevent excessive usage by |
| 674 | ** robots and random passers-by on the internet |
| 675 | */ |
| 676 | if( !g.perm.Write ){ |
| 677 | login_needed(g.anon.Admin); |
| 678 | return; |
| 679 | } |
| 680 | |
| 681 | style_header("Artifact Statistics"); |
| 682 | style_submenu_element("Repository Stats", "stat"); |
| 683 | style_submenu_element("Artifact List", "bloblist"); |
| 684 | gather_artifact_stats(1); |
| 685 | |
| 686 | db_prepare(&q, |
| 687 | "SELECT count(*), sum(isDelta), max(szCmpr)," |
| 688 | " max(szExp), sum(szCmpr), sum(szExp)" |
| 689 | " FROM artstat" |
| 690 | ); |
| 691 | db_step(&q); |
| 692 | nTotal = db_column_int(&q,0); |
| 693 | nDelta = db_column_int(&q,1); |
| 694 | nFull = nTotal - nDelta; |
| 695 | mxCmpr = db_column_int(&q, 2); |
| 696 | mxExp = db_column_int(&q, 3); |
| 697 | sumCmpr = db_column_int64(&q, 4); |
| 698 | sumExp = db_column_int64(&q, 5); |
| 699 | db_finalize(&q); |
| 700 | if( nTotal==0 ){ |
| 701 | @ No artifacts in this repository! |
| 702 | style_footer(); |
| 703 | return; |
| 704 | } |
| 705 | avgCmpr = (double)sumCmpr/nTotal; |
| 706 | avgExp = (double)sumExp/nTotal; |
| 707 | |
| 708 | db_prepare(&q, "SELECT szCmpr FROM artstat ORDER BY 1 DESC"); |
| 709 | r = 0; |
| 710 | n = 0; |
| 711 | while( db_step(&q)==SQLITE_ROW ){ |
| 712 | r += db_column_int(&q, 0); |
| 713 | if( n50pct==0 && r>=sumCmpr/2 ) n50pct = n; |
| 714 | if( n==(nTotal+99)/100 ) sz1pct = r; |
| 715 | if( n==(nTotal+9)/10 ) sz10pct = r; |
| 716 | if( n==(nTotal+4)/5 ) sz25pct = r; |
| 717 | if( n==(nTotal+1)/2 ){ sz50pct = r; medCmpr = db_column_int(&q,0); } |
| 718 | n++; |
| 719 | } |
| 720 | db_finalize(&q); |
| 721 | |
| 722 | @ <h1>Overall Artifact Size Statistics:</h1> |
| 723 | @ <table class="label-value"> |
| 724 | @ <tr><th>Number of artifacts:</th><td>%d(nTotal)</td></tr> |
| 725 | @ <tr><th>Number of deltas:</th>\ |
| 726 | @ <td>%d(nDelta) (%d(nDelta*100/nTotal)%%)</td></tr> |
| 727 | @ <tr><th>Number of full-text:</th><td>%d(nFull) \ |
| 728 | @ (%d(nFull*100/nTotal)%%)</td></tr> |
| 729 | @ <tr><th>Largest compressed artifact size:</th>\ |
| 730 | @ <td>%d(mxCmpr)</td></tr> |
| 731 | @ <tr><th>Average compressed artifact size:</th> \ |
| 732 | @ <td>%.2f(avgCmpr)</td></tr> |
| 733 | @ <tr><th>Median compressed artifact size:</th><td>%d(medCmpr)</td></tr> |
| 734 | @ <tr><th>Largest uncompressed artifact size:</td>\ |
| 735 | @ <td>%d(mxExp)</td></tr> |
| 736 | @ <tr><th>Average uncompressed artifact size:</th> \ |
| 737 | @ <td>%.2f(avgExp)</td></tr> |
| 738 | medExp = db_int(0, "SELECT szExp FROM artstat ORDER BY szExp" |
| 739 | " LIMIT 1 OFFSET %d", nTotal/2); |
| 740 | @ <tr><th>Median uncompressed artifact size:</th><td>%d(medExp)</td></tr> |
| 741 | db_prepare(&q, |
| 742 | "SELECT avg(szCmpr), max(szCmpr) FROM artstat WHERE isDelta" |
| 743 | ); |
| 744 | if( db_step(&q)==SQLITE_ROW ){ |
| 745 | @ <tr><th>Largest delta:</td>\ |
| @@ -708,62 +751,32 @@ | |
| 751 | med = db_int(0, "SELECT szCmpr FROM artstat WHERE isDelta ORDER BY szCmpr" |
| 752 | " LIMIT 1 OFFSET %d", nDelta/2); |
| 753 | @ <tr><th>Median delta:</th><td>%d(med)</td></tr> |
| 754 | } |
| 755 | db_finalize(&q); |
| 756 | r = db_double(0.0, "SELECT avg(szCmpr) FROM artstat WHERE NOT isDelta;"); |
| 757 | @ <tr><th>Average full-text artifact:</th><td>%.2f(r)</td></tr> |
| 758 | med = db_int(0, "SELECT szCmpr FROM artstat WHERE NOT isDelta ORDER BY szCmpr" |
| 759 | " LIMIT 1 OFFSET %d", nFull/2); |
| 760 | @ <tr><th>Median full-text artifact:</th><td>%d(med)</td></tr> |
| 761 | @ </table> |
| 762 | |
| 763 | @ <h1>Artifact size distribution facts:</h1> |
| 764 | @ <ol> |
| 765 | @ <li><p>The largest 1%% of artifacts (the largest %d((nTotal+99)/100) \ |
| 766 | @ artifacts) use %lld(sz1pct*100/sumCmpr)%% of the total artifact space. |
| 767 | @ <li><p>The largest 10%% of artifacts (the largest %d((nTotal+9)/10) \ |
| 768 | @ artifacts) use %lld(sz10pct*100/sumCmpr)%% of the total artifact space. |
| 769 | @ <li><p>The largest 25%% of artifacts (the largest %d(nTotal/4) \ |
| 770 | @ artifacts) use %lld(sz25pct*100/sumCmpr)%% of the total artifact space. |
| 771 | @ <li><p>The largest 50%% of artifacts (the largest %d(nTotal/2) \ |
| 772 | @ artifacts) use %lld(sz50pct*100/sumCmpr)%% of the total artifact space. |
| 773 | @ <li><p>Half of the total artifact space is used by the %d(n50pct) \ |
| 774 | @ (%.1f(n50pct*100.0/nTotal)%%) largest artifacts. |
| 775 | @ </ol> |
| 776 | |
| 777 | @ <h1>Artifact Sizes By Type:</h1> |
| 778 | db_prepare(&q, |
| 779 | "SELECT atype, count(*), sum(isDelta), sum(szCmpr), sum(szExp)" |
| 780 | " FROM artstat GROUP BY 1" |
| 781 | " UNION ALL " |
| 782 | "SELECT 'ALL', count(*), sum(isDelta), sum(szCmpr), sum(szExp)" |
| @@ -812,11 +825,11 @@ | |
| 825 | } |
| 826 | @ </tbody></table> |
| 827 | db_finalize(&q); |
| 828 | |
| 829 | if( db_exists("SELECT 1 FROM artstat WHERE atype='unknown'") ){ |
| 830 | @ <h1>Unknown Artifacts:</h1> |
| 831 | db_prepare(&q, |
| 832 | "SELECT artstat.id, blob.uuid, user.login," |
| 833 | " datetime(rcvfrom.mtime), rcvfrom.rcvid" |
| 834 | " FROM artstat JOIN blob ON artstat.id=blob.rid" |
| 835 | " LEFT JOIN rcvfrom USING(rcvid)" |
| 836 |