색인 리스트가 있는 지수 회피
26769 단어 functional
서버가 없는 체계 구조에서 지수의 회피는 큰일이다. 특히 RDS 데이터 API 등 예측할 수 없는 까다로운 병목이 존재할 때 말이다.그래서 나는 오늘 아침부터 이 문제를 해결하기 시작했고 마지막으로 색인 리스트를 사용하여 여러 서비스에 걸쳐 이 문제를 해결했다.본고에서, 나는 색인 리스트가 무엇인지 간략하게 소개한 다음에, 그것들이 어떻게 우아하고 간결하게 지수 반환 등 복잡한 문제를 해결하는데 도움을 줄 수 있는지 소개할 것이다.
색인 목록
색인 리스트는 입력 형식
i
과 출력 형식o
이 있는 리스트입니다.이 두 가지 유형은 모두 허형이다. 구체적인 값에 맞지 않고, 다른 작업 다음에 어떤 작업을 수행할 수 있는지 유형 단계에 표시하는 데 사용된다.Justin Wooa great article는 색인 목록을 사용해서 Pure Script에 햄버거를 구축하는 방법에 관한 글을 썼다.첫 번째 '입력 형식' 은 밑에 있는 bun에 대응하고, 첫 번째 출력 형식은fixings입니다.이렇게 수행됩니다.i
o
밑빵이 가능하다, ~할 수 있다,...
고정 장치
더 많은 고정이 있을 수 있어요.
더 많은 고정이 있을 수 있어요.
고기 부스러기
고기 부스러기
치즈일 수도 있어요.
치즈일 수도 있어요.
빵집
색인 목록을 사용하지 않으면
Burger BottomBun
, Burger (List Fixings)
등 형식을 사용할 수 있지만, 이 내용을 강제로 실행할 수 없습니다. order
색인 목록은 목록의 귀속 순서를 강제로 실행할 수 있습니다.순서는 네가 원하는 대로 할 수 있다. - 시작, 조건, 등등으로 순환할 수 있다.색인 규칙 적용
Justin의 예에서는 함수 서명을 사용하여 색인 규칙을 적용합니다.
placeEmptyBun :: BurgerSpec -> IxBurgerBuilder EmptyPlate BottomBunOn BurgerSpec
placeEmptyBun = addIngredient "Bottom Bun"
이런 기능을 구축하면 아주 좋은 DSL을 만들 수 있다. 만약에 한 사람이 빈 접시로 하나를 따라가려고 한다면, 그것은 실패할 것이다. 가장 야만적이고 저속한 햄버거 식자만이 이렇게 할 수 있다.그러나 야만적인 햄버거 식객 중 한 명이 나타나 새로운 함수 BottomBun
를 작성할 수도 있다.eatEmptyBun :: BurgerSpec -> IxBurgerBuilder BottomBunOn EmptyPlate BurgerSpec
eatEmptyBun = mempty
더 일반적으로, 함수 서명을 강제로 실행할 수 있는 작성 방식이 없기 때문에, 누구나 원하는 버거 building 함수를 작성할 수 있다.서로 등을 맞대다
나의 지수 회피 예시에서 나는 이런 상황이 나타나기를 원하지 않는다.나는 유형 시스템을 통해 다음 요청을 강제로 실행하는 데 항상 이전 요청보다 더 많은 시간이 걸릴 것이라고 생각한다.
eatEmptyBun
i
유형의 표를 다시 봅시다.o
i
기다리다조금만 더 기다려
조금만 더 기다려
조금 더 기다리다
조금 더 기다리다
잠깐 기다리다
잠깐 기다리다
걷어치우다
나는 아무도 가고 싶지 않다
o
.이로 인해 AWS가 다시 붕괴될 수 있습니다.구원을 위해 노력하다
TypeClass는 컴파일러에 존재하는 표일 뿐입니다. 열은 제약할 형식이고 줄은 제약입니다.따라서 우리는 typeclass를 사용하여
Wait a small eternity -> Wait a bit -> Wait a bit -> Wait a bit -> Wait a bit -> Give up
i
유형을 구속할 수 있다. 이것은 당연한 것이다.따라서 만약에 우리의 색인 목록이 o
이라면 우리의 유형 클래스는 다음과 같다.class ExponentialBackoff i t where
continue :: forall m a. m i i a -> m i t a
instance exponentialBackoff0 :: WaitABit GiveUp
continue :: ...
instance exponentialBackoff1 :: WaitABitLonger GiveUp
instance exponentialBackoff2 :: WaitEvenLonger GiveUp
여기에 많은 샘플 파일이 있기 때문에 나는 Program i o a
로 실제 코드 라이브러리의 귀속 관계를 표시하기로 결정했다.class ExponentialBackoff i where
continue :: forall m a. m i i a -> m i Z a
instance exponentialBackoff0 :: Z
continue :: ...
instance exponentialBackoff1 :: (Succ i)
purescript-typelevel-peano
함수는 continue
블록의 끝에 나타난다. 이것은 우리의 프로그램이 하나의 유형I.do
으로 실행을 마치면 m (Succ i) (Succ i) a
는 우리가 진행하고 있는 반환 절차이다. 즉, (Succ i) (Succ i)
는(Succ (Succ Z))
우리의 peano 카운트다운에서 2
까지 떨어진다는 것을 의미한다. m i i a
는i
은Z
(0)이다.이 때 continue
에 실행Z
됩니다.우리의 코드 라이브러리에서, 이것은 프로그램의 종료와 우아한 청소이다.정수의 typelevel을 사용하여 나타내는 또 다른 장점은
reflectNat
를 사용하여 유형에서 지수의 회피 값을 직접 계산할 수 있다는 것이다.전체 예
비록 우리의 백엔드 코드 라이브러리 (불행하게도) 는 아직 시작되지 않았지만, 우리는 생산 과정에서 이 물건들을 어떻게 사용하는지 예시를 제공하기 위해
S3Squirrel
라는 소형 ish 서비스를 사용해 왔다.Google 서비스에서, Google은 backoff
유형 클래스의 Backoff
함수를 사용하여 1원 상하문에서 정확한 지수 반환을 얻습니다.우리가 이 과정을 추진한 이후로, 우리는 AWS 융해 사건이 발생하지 않았다.w00t!💯data S3SquirrelProgramF a
= GetETagForResource String (Maybe String -> a)
| GetS3InfoFromDBOnCacheHit String (Maybe String) (Maybe S3Info -> a)
| DownloadResourceToFile String String (Unit -> a)
| ReadFileToBuffer String (Buffer -> a)
| GenerateUUID (String -> a)
| FreeDelay Number (Unit -> a)
| UploadObjectToS3 String String Buffer (Unit -> a)
| WriteEtagAndS3InfoToDb String (Maybe String) String String (Either Error Unit -> a)
| FreeLog String (Unit -> a)
| FreeThrow Error (Unit -> a)
derive instance functorS3SquirrelProgramF :: Functor S3SquirrelProgramF
derive instance genericS3SquirrelProgramF :: Generic (S3SquirrelProgramF a) _
f :: forall i. Constructors S3SquirrelProgramF (S3SquirrelProgram i i)
f = constructors (wrap <<< liftF :: S3SquirrelProgramF ~> (S3SquirrelProgram i i))
newtype S3SquirrelProgram i o a
= S3SquirrelProgram (Free S3SquirrelProgramF a)
derive instance newtypeS3SquirrelProgram :: Newtype (S3SquirrelProgram i o a) _
instance ixApplicativeS3SquirrelProgram :: IxApplicative S3SquirrelProgram where
ipure = wrap <<< pure
instance ixFunctorS3SquirrelProgram :: IxFunctor S3SquirrelProgram where
imap fx (S3SquirrelProgram x) = S3SquirrelProgram (map fx x)
instance ixApplyS3SquirrelProgram :: IxApply S3SquirrelProgram where
iapply (S3SquirrelProgram fx) (S3SquirrelProgram x) = S3SquirrelProgram (apply fx x)
instance ixBindS3SquirrelProgram :: IxBind S3SquirrelProgram where
ibind (S3SquirrelProgram x) fx = wrap (x >>= (unwrap <<< fx))
instance ixMonadS3SquirrelProgram :: IxMonad S3SquirrelProgram
type SProg (x :: Nat) (y :: Nat)
= S3SquirrelProgram (NProxy x) (NProxy y) Unit
class ProgramOutcome (i :: Nat) where
s3SquirrelNext :: Error -> ResourceInfo -> SProg i Z
class MaxRetries t (i :: Nat) | t -> i
instance maxRetriesS3SquirrelProgram :: MaxRetries (S3SquirrelProgram i i a) (Succ (Succ (Succ Z)))
class Backoff m where
backoff :: (Number -> m Unit) -> m Unit
instance backoffS3SquirrelProgram :: (IsNat x, IsNat i, MaxRetries (S3SquirrelProgram (NProxy i) (NProxy i) a) x) => Backoff (S3SquirrelProgram (NProxy i) (NProxy i)) where
backoff = (#) (2.0 `pow` (toNumber (maxRetries - currentPos)))
where
maxRetries = reflectNat (NProxy :: NProxy x)
currentPos = reflectNat (NProxy :: NProxy i)
instance programOutcomeZ :: ProgramOutcome Z where
s3SquirrelNext e _ = f.freeThrow e
instance programOutcomeSucc :: (IsNat x, ProgramOutcome x) => ProgramOutcome (Succ x) where
s3SquirrelNext e ri =
( Ix.do
S3SquirrelProgram $ pure unit :: SProg (Succ x) x
s3SquirrelProgram ri :: SProg x Z
)
downloadAndWriteToDb :: forall (i :: Nat). IsNat i => ProgramOutcome i => String -> Maybe String -> SProg i Z
downloadAndWriteToDb resourceUrl etag = Ix.do
uuid <- f.generateUUID
let
filename = "/tmp/" <> uuid
f.downloadResourceToFile resourceUrl filename
buffer <- f.readFileToBuffer filename
f.uploadObjectToS3 mknBucket uuid buffer
f.freeLog ("writing to db " <> resourceUrl <> " etag: " <> show etag)
res <-
f.writeEtagAndS3InfoToDb
resourceUrl
etag
mknBucket
uuid
case res of
Left err -> Ix.do
f.freeLog (show err)
backoff f.freeDelay
s3SquirrelNext err { resourceUrl }
Right _ -> S3SquirrelProgram (pure unit)
s3SquirrelProgram :: forall (i :: Nat). IsNat i => ProgramOutcome i => ResourceInfo -> SProg i Z
s3SquirrelProgram { resourceUrl } = Ix.do
etag <- f.getETagForResource resourceUrl
f.freeLog ("got etag " <> show etag <> " for resource " <> resourceUrl)
s3Info' <- f.getS3InfoFromDBOnCacheHit resourceUrl etag
f.freeLog ("got s3info " <> show s3Info')
case s3Info' of
Nothing -> downloadAndWriteToDb resourceUrl etag
Just { bucket, key } -> S3SquirrelProgram (pure unit)
Reference
이 문제에 관하여(색인 리스트가 있는 지수 회피), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/mikesol/exponential-backoff-with-indexed-monads-5fk3텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)