mirror of
https://github.com/facebook/sapling.git
synced 2024-10-09 16:31:02 +03:00
scmstore: collect API usage metrics
Summary: Instrument all the fetching-related legacy API implementations (ie, not `get_logged_fetches` and `get_shared_mutable`) to track the number of calls, keys, and single-key calls. This introduces an additional lock acquisition to each of these implementations (and it'd be awkward to merge it with the one in `fetch`), but I think that's probably fine. For APIs which do not support a variable number of keys, I use `.call(0)` so we simply track the total number of API calls. Reviewed By: DurhamG Differential Revision: D30003444 fbshipit-source-id: 8756d2669ca038b3f6a08e211e44e8ccb9251312
This commit is contained in:
parent
d9a885a6df
commit
d4e6c15939
@ -70,6 +70,11 @@ impl FetchMetrics {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(meyer): I don't think this is in any critical paths, but it'd be nicer to rewrite this
|
||||
// to use `Item = (Vec<&'static str>, usize)` instead of `Item = (String, usize)`, since all
|
||||
// the fields are indeed statically named right now, or, better, just tree of some sort instead of a
|
||||
// list of metrics. Probably appropriate for a `SmallVec` too, since the namespace depth is
|
||||
// limited.
|
||||
fn namespaced(
|
||||
namespace: &'static str,
|
||||
metrics: impl Iterator<Item = (impl AsRef<str>, usize)>,
|
||||
@ -240,10 +245,111 @@ impl FileStoreWriteMetrics {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ApiMetrics {
|
||||
/// Number of calls to this API
|
||||
calls: usize,
|
||||
|
||||
/// Total number of entities requested across all calls
|
||||
keys: usize,
|
||||
|
||||
/// Number of calls for only a single entity
|
||||
singles: usize,
|
||||
}
|
||||
|
||||
impl AddAssign for ApiMetrics {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.calls += rhs.calls;
|
||||
self.keys += rhs.keys;
|
||||
self.singles += rhs.singles;
|
||||
}
|
||||
}
|
||||
|
||||
impl ApiMetrics {
|
||||
pub(crate) fn call(&mut self, keys: usize) {
|
||||
self.calls += 1;
|
||||
self.keys += keys;
|
||||
if keys == 1 {
|
||||
self.singles += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn metrics(&self) -> impl Iterator<Item = (&'static str, usize)> {
|
||||
std::array::IntoIter::new([
|
||||
("calls", self.calls),
|
||||
("keys", self.keys),
|
||||
("singles", self.singles),
|
||||
])
|
||||
.filter(|&(_, v)| v != 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct FileStoreApiMetrics {
|
||||
pub(crate) hg_getfilecontent: ApiMetrics,
|
||||
pub(crate) hg_addpending: ApiMetrics,
|
||||
pub(crate) hg_commitpending: ApiMetrics,
|
||||
pub(crate) hg_get: ApiMetrics,
|
||||
pub(crate) hg_getmeta: ApiMetrics,
|
||||
pub(crate) hg_refresh: ApiMetrics,
|
||||
pub(crate) hg_prefetch: ApiMetrics,
|
||||
pub(crate) hg_upload: ApiMetrics,
|
||||
pub(crate) hg_getmissing: ApiMetrics,
|
||||
pub(crate) hg_add: ApiMetrics,
|
||||
pub(crate) hg_flush: ApiMetrics,
|
||||
pub(crate) contentdatastore_blob: ApiMetrics,
|
||||
pub(crate) contentdatastore_metadata: ApiMetrics,
|
||||
}
|
||||
|
||||
impl AddAssign for FileStoreApiMetrics {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.hg_getfilecontent += rhs.hg_getfilecontent;
|
||||
self.hg_addpending += rhs.hg_addpending;
|
||||
self.hg_commitpending += rhs.hg_commitpending;
|
||||
self.hg_get += rhs.hg_get;
|
||||
self.hg_getmeta += rhs.hg_getmeta;
|
||||
self.hg_refresh += rhs.hg_refresh;
|
||||
self.hg_prefetch += rhs.hg_prefetch;
|
||||
self.hg_upload += rhs.hg_upload;
|
||||
self.hg_getmissing += rhs.hg_getmissing;
|
||||
self.hg_add += rhs.hg_add;
|
||||
self.hg_flush += rhs.hg_flush;
|
||||
self.contentdatastore_blob += rhs.contentdatastore_blob;
|
||||
self.contentdatastore_metadata += rhs.contentdatastore_metadata;
|
||||
}
|
||||
}
|
||||
|
||||
impl FileStoreApiMetrics {
|
||||
fn metrics(&self) -> impl Iterator<Item = (String, usize)> {
|
||||
namespaced("hg_getfilecontent", self.hg_getfilecontent.metrics())
|
||||
.chain(namespaced("hg_addpending", self.hg_addpending.metrics()))
|
||||
.chain(namespaced(
|
||||
"hg_commitpending",
|
||||
self.hg_commitpending.metrics(),
|
||||
))
|
||||
.chain(namespaced("hg_get", self.hg_get.metrics()))
|
||||
.chain(namespaced("hg_getmeta", self.hg_getmeta.metrics()))
|
||||
.chain(namespaced("hg_refresh", self.hg_refresh.metrics()))
|
||||
.chain(namespaced("hg_prefetch", self.hg_prefetch.metrics()))
|
||||
.chain(namespaced("hg_upload", self.hg_upload.metrics()))
|
||||
.chain(namespaced("hg_getmissing", self.hg_getmissing.metrics()))
|
||||
.chain(namespaced("hg_add", self.hg_add.metrics()))
|
||||
.chain(namespaced(
|
||||
"contentdatastore_blob",
|
||||
self.contentdatastore_blob.metrics(),
|
||||
))
|
||||
.chain(namespaced(
|
||||
"contentdatastore_metadata",
|
||||
self.contentdatastore_metadata.metrics(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct FileStoreMetrics {
|
||||
pub(crate) fetch: FileStoreFetchMetrics,
|
||||
pub(crate) write: FileStoreWriteMetrics,
|
||||
pub(crate) api: FileStoreApiMetrics,
|
||||
}
|
||||
|
||||
impl FileStoreMetrics {
|
||||
@ -255,7 +361,8 @@ impl FileStoreMetrics {
|
||||
namespaced(
|
||||
"scmstore.file",
|
||||
namespaced("fetch", self.fetch.metrics())
|
||||
.chain(namespaced("write", self.write.metrics())),
|
||||
.chain(namespaced("write", self.write.metrics()))
|
||||
.chain(namespaced("api", self.api.metrics())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -456,6 +456,7 @@ impl LegacyStore for FileStore {
|
||||
|
||||
#[instrument(skip(self))]
|
||||
fn get_file_content(&self, key: &Key) -> Result<Option<Bytes>> {
|
||||
self.metrics.write().api.hg_getfilecontent.call(0);
|
||||
self.fetch(std::iter::once(key.clone()), FileAttributes::CONTENT)
|
||||
.single()?
|
||||
.map(|entry| entry.content.unwrap().file_content())
|
||||
@ -471,6 +472,7 @@ impl LegacyStore for FileStore {
|
||||
meta: Metadata,
|
||||
location: RepackLocation,
|
||||
) -> Result<()> {
|
||||
self.metrics.write().api.hg_addpending.call(0);
|
||||
if let Some(contentstore) = self.contentstore.as_ref() {
|
||||
contentstore.add_pending(key, data, meta, location)
|
||||
} else {
|
||||
@ -488,6 +490,7 @@ impl LegacyStore for FileStore {
|
||||
}
|
||||
|
||||
fn commit_pending(&self, location: RepackLocation) -> Result<Option<Vec<PathBuf>>> {
|
||||
self.metrics.write().api.hg_commitpending.call(0);
|
||||
if let Some(contentstore) = self.contentstore.as_ref() {
|
||||
contentstore.commit_pending(location)
|
||||
} else {
|
||||
@ -500,6 +503,7 @@ impl LegacyStore for FileStore {
|
||||
impl HgIdDataStore for FileStore {
|
||||
// Fetch the raw content of a single TreeManifest blob
|
||||
fn get(&self, key: StoreKey) -> Result<StoreResult<Vec<u8>>> {
|
||||
self.metrics.write().api.hg_get.call(0);
|
||||
Ok(
|
||||
match self
|
||||
.fetch(
|
||||
@ -515,6 +519,7 @@ impl HgIdDataStore for FileStore {
|
||||
}
|
||||
|
||||
fn get_meta(&self, key: StoreKey) -> Result<StoreResult<Metadata>> {
|
||||
self.metrics.write().api.hg_getmeta.call(0);
|
||||
Ok(
|
||||
match self
|
||||
.fetch(
|
||||
@ -530,6 +535,7 @@ impl HgIdDataStore for FileStore {
|
||||
}
|
||||
|
||||
fn refresh(&self) -> Result<()> {
|
||||
self.metrics.write().api.hg_refresh.call(0);
|
||||
// AFAIK refresh only matters for DataPack / PackStore
|
||||
Ok(())
|
||||
}
|
||||
@ -537,6 +543,7 @@ impl HgIdDataStore for FileStore {
|
||||
|
||||
impl RemoteDataStore for FileStore {
|
||||
fn prefetch(&self, keys: &[StoreKey]) -> Result<Vec<StoreKey>> {
|
||||
self.metrics.write().api.hg_prefetch.call(keys.len());
|
||||
Ok(self
|
||||
.fetch(
|
||||
keys.iter().cloned().filter_map(|sk| sk.maybe_into_key()),
|
||||
@ -549,6 +556,7 @@ impl RemoteDataStore for FileStore {
|
||||
}
|
||||
|
||||
fn upload(&self, keys: &[StoreKey]) -> Result<Vec<StoreKey>> {
|
||||
self.metrics.write().api.hg_upload.call(keys.len());
|
||||
// TODO(meyer): Eliminate usage of legacy API, or at least minimize it (do we really need memcache + multiplex, etc)
|
||||
if let Some(ref lfs_remote) = self.lfs_remote {
|
||||
let mut multiplex = MultiplexDeltaStore::new();
|
||||
@ -570,6 +578,7 @@ impl RemoteDataStore for FileStore {
|
||||
|
||||
impl LocalStore for FileStore {
|
||||
fn get_missing(&self, keys: &[StoreKey]) -> Result<Vec<StoreKey>> {
|
||||
self.metrics.write().api.hg_getmissing.call(keys.len());
|
||||
Ok(self
|
||||
.local()
|
||||
.fetch(
|
||||
@ -585,6 +594,7 @@ impl LocalStore for FileStore {
|
||||
|
||||
impl HgIdMutableDeltaStore for FileStore {
|
||||
fn add(&self, delta: &Delta, metadata: &Metadata) -> Result<()> {
|
||||
self.metrics.write().api.hg_add.call(0);
|
||||
if let Delta {
|
||||
data,
|
||||
base: None,
|
||||
@ -598,6 +608,7 @@ impl HgIdMutableDeltaStore for FileStore {
|
||||
}
|
||||
|
||||
fn flush(&self) -> Result<Option<Vec<PathBuf>>> {
|
||||
self.metrics.write().api.hg_flush.call(0);
|
||||
self.flush()?;
|
||||
Ok(None)
|
||||
}
|
||||
@ -607,6 +618,7 @@ impl HgIdMutableDeltaStore for FileStore {
|
||||
// that if available, but I feel like there's probably something wrong if this is called for trees.
|
||||
impl ContentDataStore for FileStore {
|
||||
fn blob(&self, key: StoreKey) -> Result<StoreResult<Bytes>> {
|
||||
self.metrics.write().api.contentdatastore_blob.call(0);
|
||||
Ok(
|
||||
match self
|
||||
.fetch(
|
||||
@ -622,6 +634,7 @@ impl ContentDataStore for FileStore {
|
||||
}
|
||||
|
||||
fn metadata(&self, key: StoreKey) -> Result<StoreResult<ContentMetadata>> {
|
||||
self.metrics.write().api.contentdatastore_metadata.call(0);
|
||||
Ok(
|
||||
match self
|
||||
.fetch(
|
||||
|
@ -24,7 +24,13 @@ Clone it
|
||||
fetching tree '' a539ce0c1a22b0ecf34498f9f5ce8ea56df9ecb7
|
||||
1 trees fetched over * (glob)
|
||||
2 files fetched over 1 fetches - (2 misses, 0.00% hit ratio) over * (glob) (?)
|
||||
{ metrics : { scmstore : { file : { fetch : { contentstore : { hits : 2,
|
||||
{ metrics : { scmstore : { file : { api : { hg : { getfilecontent : { calls : 2},
|
||||
getmeta : { calls : 2},
|
||||
getmissing : { calls : 1,
|
||||
keys : 2},
|
||||
prefetch : { calls : 3,
|
||||
keys : 4}}},
|
||||
fetch : { contentstore : { hits : 2,
|
||||
keys : 2,
|
||||
requests : 1},
|
||||
indexedlog : { cache : { hits : 8,
|
||||
@ -86,7 +92,8 @@ Pull exactly up to d into the client
|
||||
adding manifests
|
||||
adding file changes
|
||||
added 2 changesets with 0 changes to 0 files
|
||||
{ metrics : { ssh : { connections : 1,
|
||||
{ metrics : { scmstore : { file : { api : { hg : { prefetch : { calls : 1}}}}},
|
||||
ssh : { connections : 1,
|
||||
read : { bytes : 1086},
|
||||
write : { bytes : 680}}}}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user