glean-0.1.0.0: glean/glass/Glean/Glass/Main.hs
{-
Copyright (c) Meta Platforms, Inc. and affiliates.
All rights reserved.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree.
-}
{-# LANGUAGE CPP #-}
module Glean.Glass.Main
( mainWith
-- * Useful helpers
, withEnv
) where
import Control.Concurrent (myThreadId)
import Control.Trace (traceMsg, (>$<))
import Data.Hashable (hash)
import Data.List (stripPrefix)
import Facebook.Service ( runFacebookService' )
import Facebook.Fb303 ( fb303Handler, withFb303 )
import System.Timeout
import TextShow
import Thrift.Channel (Header)
import Thrift.Protocol.ApplicationException.Types as Thrift
#ifdef FBTHRIFT
import qualified Thrift.Server.CppServer as Thrift
#else
import qualified Thrift.Server.HTTP as Thrift
#endif
import Util.EventBase ( withEventBaseDataplane )
import Util.Log.Text ( logInfo )
import Logger.IO (withLogger)
import Control.Exception (SomeException, fromException, throwIO)
import Data.Text (Text)
import Data.Text.Encoding (encodeUtf8)
import Options.Applicative (Parser)
import qualified Glean
import Glean.Init ( withOptions )
import Glean.LocalOrRemote as Glean
( Service(Remote), withBackendWithDefaultOptions )
import Glean.Schema.Builtin.Types (schema_id)
import Glean.Util.ConfigProvider
( ConfigProvider(defaultConfigOptions, withConfigProvider) )
import Glean.Util.Some ( Some(Some) )
import Glean.Glass.RepoMapping -- site-specific
import qualified Glean.Glass.Env as Glass
import Glean.Glass.Repos (withLatestRepos)
import qualified Glean.Glass.Options as Glass
import qualified Glean.Glass.Handler.Documents as Handler
import qualified Glean.Glass.Handler.Symbols as Handler
import Glean.Glass.GlassService.Service ( GlassServiceCommand(..) )
import Glean.Glass.Types
( GlassException (GlassException, glassException_reasons),
GlassExceptionReason (..))
import Glean.Glass.Env (Env'(tracer), Env)
import Glean.Glass.Tracer ( isTracingEnabled )
import Glean.Glass.Handler.Cxx as Cxx
import Glean.Glass.Tracing
(GlassTrace(TraceCommand), GlassTraceWithId (GlassTraceWithId))
kThriftCacheNoCache :: Text
kThriftCacheNoCache = "nocache"
-- | Ok, go.
mainWith
:: Parser (Glass.Config GlassTraceWithId -> Glass.Config GlassTraceWithId)
-> IO ()
mainWith = withGlass runGlass
type Server trace = Glass.Env' trace -> Glass.Config trace -> IO ()
-- | Set up the resources we'll need
withGlass
:: TextShow trace
=> Server trace -> Parser (Glass.Config trace -> Glass.Config trace) -> IO ()
withGlass f backendsP =
withOptions (Glass.options backendsP) $ \config ->
withEnv config Nothing $ \env -> f env config
-- | Construct a server environment
withEnv
:: TextShow trace
=> Glass.Config trace
-> Maybe Glean.Repo
-> (Glass.Env' trace -> IO a)
-> IO a
withEnv Glass.Config{..} gleanDB f =
withEventBaseDataplane $ \evp ->
withConfigProvider defaultConfigOptions $ \cfgapi ->
withLogger cfgapi $ \logger ->
withFb303 serviceName $ \fb303 ->
withBackendWithDefaultOptions evp cfgapi gleanService (Just schema_id)
$ \backend -> do
scm <- sourceControl evp
snapshotBackend <- snapshotBackend evp
hst <- haxlState evp
withLatestRepos backend scm (Just logger)
(if isRemote gleanService then listDatabasesRetry else Nothing) refreshFreq
$ \latestGleanRepos -> do
repoMapping <- getRepoMapping
f Glass.Env
{ gleanBackend = Some backend
, gleanDB = gleanDB
, snapshotBackend = snapshotBackend
, sourceControl = scm
, haxlState = hst
, ..
}
-- | Some of the retry/logging logic only applies to remote services
isRemote :: Service -> Bool
isRemote service = case service of
Remote{} -> True
_ -> False
-- | Kick off the server
runGlass :: Server GlassTraceWithId
runGlass res@Glass.Env{fb303, evp} conf@Glass.Config{..} = do
logInfo =<< welcomeMessage evp conf
let options = Thrift.defaultOptions
{ Thrift.desiredPort = Just listenPort
, Thrift.numWorkerThreads = numWorkerThreads
}
runFacebookService' fb303 (glassHandler res) assignHeaders options
assignHeaders :: GlassServiceCommand r -> Either SomeException r -> Header
assignHeaders _ (Left e) | isRevisionNotAvailableException e =
[ (encodeUtf8 kThriftCacheNoCache, "1")]
where
isRevisionNotAvailableException e = case fromException e of
Just GlassException{glassException_reasons} ->
all isRevisionNotAvailable glassException_reasons
_ -> False
isRevisionNotAvailable e = case e of
GlassExceptionReason_exactRevisionNotAvailable{} -> True
GlassExceptionReason_matchingRevisionNotAvailable{} -> True
_ -> False
assignHeaders _ _ = []
-- | Perform an operation with the latest RepoMapping
withCurrentRepoMapping :: Glass.Env -> (Glass.Env -> IO a) -> IO a
withCurrentRepoMapping env0 fn = do
current <- getRepoMapping
fn (env0 { Glass.repoMapping = current })
withRequestTracing :: Env' GlassTraceWithId -> (Env -> IO b) -> IO b
withRequestTracing env k = do
enabled <- isTracingEnabled
if enabled
then do
-- Fix the trace id for this request traces.
-- Since every request is handled by a fresh new thread,
-- we reuse thread ids as trace ids
tid <- tidToIndex <$> myThreadId
k env{tracer = GlassTraceWithId tid >$< tracer env}
else k env { tracer = mempty }
where
tidToIndex tid = case stripPrefix "ThreadId " (show tid) of
Just n | [(i, "")] <- reads n -> i
_ -> hash tid -- should be unreachable
-- Actual glass service handler, types from glass.thrift
-- TODO: snapshot the env, rather than passing in the mutable fields
--
glassHandler :: Glass.Env' GlassTraceWithId -> GlassServiceCommand r -> IO r
glassHandler env0 cmd =
Glass.withAllocationLimit env0 $
withRequestTracing env0 $ \env1 ->
withCurrentRepoMapping env1 $ \env ->
tracing env $
withTimeout $ case cmd of
SuperFacebookService r -> fb303Handler (Glass.fb303 env) r
-- Listing symbols in files
DocumentSymbolListX r opts -> Handler.documentSymbolListX env r opts
DocumentSymbolIndex r opts -> Handler.documentSymbolIndex env r opts
-- Navigating
FindReferenceRanges r opts -> Handler.findReferenceRanges env r opts
-- Symbol info
DescribeSymbol r opts -> Handler.describeSymbol env r opts
SymbolLocation r opts -> Handler.symbolLocation env r opts
ResolveSymbols r opts -> Handler.resolveSymbols env r opts
-- Search for symbols
-- by string
SearchSymbol r opts -> Handler.searchSymbol env r opts
-- by relationship
SearchRelated r opts req -> Handler.searchRelated env r opts req
SearchRelatedNeighborhood r opts req ->
Handler.searchRelatedNeighborhood env r opts req
-- C++/LSP specific
FileIncludeLocations r opts -> Cxx.fileIncludeLocations env r opts
ClangUSRToDefinition r opts -> fst <$> Cxx.clangUSRToDefinition env r opts
UsrToDefinition r opts -> fst <$> Cxx.usrToDefinition env r opts
where
tracing env
| SuperFacebookService{} <- cmd = id
| otherwise = traceMsg (tracer env) (TraceCommand cmd)
withTimeout act = do
-- workaround for lack of request cancellation on client-side timeout
result <- timeout clientTimeoutMs act
case result of
Nothing ->
throwIO $
Thrift.ApplicationException
"glass server timeout"
Thrift.ApplicationExceptionType_Timeout
Just r -> return r
clientTimeoutMs =
-- TODO get the client timeout from the Thrift request
30 * 1000000