eden debug blob add option to show blob contents from all locations

Summary:
EdenFS now supports fetching blobs from multiple locations. It certainly would
have been helpful to be able to see blob contents from all locations all at
once while debugging reports of corrupted file content recently.

Lets add an option to `eden debug blob` so that we cans see data from multiple
places at once.

Reviewed By: chadaustin

Differential Revision: D41165544

fbshipit-source-id: 421fc8841a531894715ff5cbb3786e1003782666
This commit is contained in:
Katie Mancini 2022-12-06 23:42:55 -08:00 committed by Facebook GitHub Bot
parent e9da48a6cd
commit 224cde1d5c

View File

@ -48,6 +48,7 @@ from facebook.eden.ttypes import (
MountId, MountId,
NoValueForKeyError, NoValueForKeyError,
ScmBlobOrError, ScmBlobOrError,
ScmBlobWithOrigin,
SyncBehavior, SyncBehavior,
TimeSpec, TimeSpec,
TreeInodeDebugInfo, TreeInodeDebugInfo,
@ -367,12 +368,74 @@ class BlobCmd(Subcmd):
default=False, default=False,
help="Only fetch the data from the servers. ", help="Only fetch the data from the servers. ",
) )
group.add_argument(
"-a",
"--all",
action="store_true",
default=False,
help="Fetch the blob from all storage locations and display their contents. ",
)
parser.add_argument( parser.add_argument(
"mount", "mount",
help="The EdenFS mount point path.", help="The EdenFS mount point path.",
) )
parser.add_argument("id", help="The blob ID") parser.add_argument("id", help="The blob ID")
def origin_to_text(self, origin: DataFetchOrigin) -> str:
if origin == DataFetchOrigin.MEMORY_CACHE:
return "object cache"
elif origin == DataFetchOrigin.DISK_CACHE:
return "local store"
elif origin == DataFetchOrigin.LOCAL_BACKING_STORE:
return "hgcache"
elif origin == DataFetchOrigin.REMOTE_BACKING_STORE:
return "servers"
elif origin == DataFetchOrigin.ANYWHERE:
return "EdenFS production data fetching process"
return "<unknown>"
def print_blob_or_error(self, blobOrError: ScmBlobOrError) -> None:
if blobOrError.getType() == ScmBlobOrError.BLOB:
sys.stdout.buffer.write(blobOrError.get_blob())
else:
error = blobOrError.get_error()
sys.stdout.buffer.write(f"ERROR fetching data: {error}\n".encode())
def print_all_blobs(self, blobs: List[ScmBlobWithOrigin]) -> None:
non_error_blobs = []
for blob in blobs:
blob_found = blob.blob.getType() == ScmBlobOrError.BLOB
pretty_origin = self.origin_to_text(blob.origin)
pretty_blob_found = "hit" if blob_found else "miss"
print(f"{pretty_origin}: {pretty_blob_found}")
if blob_found:
non_error_blobs.append(blob)
if len(non_error_blobs) == 0:
return
if len(non_error_blobs) == 1:
print("\n")
sys.stdout.buffer.write(non_error_blobs[0].blob.get_blob())
return
blobs_match = True
for blob in non_error_blobs[1::]:
if blob.blob.get_blob() != non_error_blobs[0].blob.get_blob():
blobs_match = False
break
if blobs_match:
print("\nAll blobs match :) \n")
sys.stdout.buffer.write(non_error_blobs[0].blob.get_blob())
else:
print("\n!!!!! Blob mismatch !!!!! \n")
for blob in non_error_blobs:
prety_fromwhere = self.origin_to_text(blob.origin)
print(f"Blob from {prety_fromwhere}\n")
print("-----------------------------\n")
sys.stdout.buffer.write(blob.blob.get_blob())
print("\n-----------------------------\n\n")
def run(self, args: argparse.Namespace) -> int: def run(self, args: argparse.Namespace) -> int:
instance, checkout, _rel_path = cmd_util.require_checkout(args, args.mount) instance, checkout, _rel_path = cmd_util.require_checkout(args, args.mount)
blob_id = parse_object_id(args.id) blob_id = parse_object_id(args.id)
@ -386,6 +449,14 @@ class BlobCmd(Subcmd):
origin_flags = DataFetchOrigin.LOCAL_BACKING_STORE origin_flags = DataFetchOrigin.LOCAL_BACKING_STORE
elif args.remote_only: elif args.remote_only:
origin_flags = DataFetchOrigin.REMOTE_BACKING_STORE origin_flags = DataFetchOrigin.REMOTE_BACKING_STORE
elif args.all:
origin_flags = (
DataFetchOrigin.MEMORY_CACHE
| DataFetchOrigin.DISK_CACHE
| DataFetchOrigin.LOCAL_BACKING_STORE
| DataFetchOrigin.REMOTE_BACKING_STORE
| DataFetchOrigin.ANYWHERE
)
with instance.get_thrift_client_legacy() as client: with instance.get_thrift_client_legacy() as client:
data = client.debugGetBlob( data = client.debugGetBlob(
@ -395,11 +466,11 @@ class BlobCmd(Subcmd):
origin_flags, origin_flags,
) )
) )
blobOrError = data.blobs[0].blob if args.all:
if blobOrError.getType() == ScmBlobOrError.BLOB: self.print_all_blobs(data.blobs)
sys.stdout.buffer.write(blobOrError.get_blob()) else:
else: self.print_blob_or_error(data.blobs[0].blob)
raise blobOrError.get_error()
return 0 return 0