/// <summary> /// Insert a response from AVERT into the store /// </summary> /// <param name="storageConsistencyMode">consistency to use</param> /// <param name="reportHandle">uniquely identifies this report</param> /// <param name="responseTime">when the response was received from AVERT</param> /// <param name="responseBody">body of the response from AVERT</param> /// <param name="entity">AVERT transaction entity to update</param> /// <returns>a task that inserts the response into the store</returns> public async Task InsertResponse( StorageConsistencyMode storageConsistencyMode, string reportHandle, DateTime responseTime, string responseBody, IAVERTTransactionEntity entity) { // check inputs if (string.IsNullOrWhiteSpace(reportHandle)) { throw new ArgumentNullException("reportHandle"); } if (entity == null) { throw new ArgumentNullException("entity"); } // get the table interface CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.AVERT); ObjectTable lookupTable = this.tableStoreManager.GetTable(ContainerIdentifier.AVERT, TableIdentifier.AVERTLookup) as ObjectTable; // update the entity entity.ResponseTime = responseTime; entity.ResponseBody = responseBody; // update it in the store await store.ExecuteOperationAsync(Operation.Replace(lookupTable, reportHandle, reportHandle, entity as AVERTTransactionEntity), storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Delete popular topic /// </summary> /// <param name="storageConsistencyMode">Storage consistency mode</param> /// <param name="timeRange">Time range</param> /// <param name="hostAppHandle">Hosting app handle: Master app or client app</param> /// <param name="appHandle">App handle</param> /// <param name="topicHandle">Topic handle</param> /// <param name="topicUserHandle">User handle</param> /// <returns>Delete popular topic task</returns> public async Task DeletePopularTopic( StorageConsistencyMode storageConsistencyMode, TimeRange timeRange, string hostAppHandle, string appHandle, string topicHandle, string topicUserHandle) { TopicRankFeedEntity topicRankFeedEntity = new TopicRankFeedEntity() { AppHandle = appHandle, TopicHandle = topicHandle, UserHandle = topicUserHandle }; var serializedEntity = StoreSerializers.MinimalTopicRankFeedEntitySerialize(topicRankFeedEntity); CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.PopularTopics); Transaction transaction = new Transaction(); RankFeedTable topicsTable = this.tableStoreManager.GetTable(ContainerIdentifier.PopularTopics, TableIdentifier.PopularTopicsFeed) as RankFeedTable; string feedKey = this.GetPopularTopicsFeedKey(timeRange, hostAppHandle); transaction.Add(Operation.DeleteIfExists(topicsTable, ContainerIdentifier.PopularTopics.ToString(), feedKey, serializedEntity)); if (timeRange != TimeRange.AllTime) { RankFeedTable expirationsTable = this.tableStoreManager.GetTable(ContainerIdentifier.PopularTopics, TableIdentifier.PopularTopicsExpirationsFeed) as RankFeedTable; transaction.Add(Operation.DeleteIfExists(expirationsTable, ContainerIdentifier.PopularTopics.ToString(), feedKey, serializedEntity)); } await store.ExecuteTransactionAsync(transaction, storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Insert a new submission to AVERT into the store /// </summary> /// <param name="storageConsistencyMode">consistency to use</param> /// <param name="reportHandle">uniquely identifies this report</param> /// <param name="appHandle">uniquely identifies the app that the user is in</param> /// <param name="reportType">is the complaint against a user or content?</param> /// <param name="requestTime">when the request was submitted to AVERT</param> /// <param name="requestBody">body of the request to AVERT</param> /// <returns>a task that inserts the submission into the store</returns> public async Task InsertSubmission( StorageConsistencyMode storageConsistencyMode, string reportHandle, string appHandle, ReportType reportType, DateTime requestTime, string requestBody) { // check input if (string.IsNullOrWhiteSpace(reportHandle)) { throw new ArgumentNullException("reportHandle"); } // get the table interface CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.AVERT); ObjectTable lookupTable = this.tableStoreManager.GetTable(ContainerIdentifier.AVERT, TableIdentifier.AVERTLookup) as ObjectTable; // create the entity that will be inserted into the table AVERTTransactionEntity avertTransactionEntity = new AVERTTransactionEntity() { ReportHandle = reportHandle, AppHandle = appHandle, ReportType = reportType, RequestTime = requestTime, RequestBody = requestBody, ResponseTime = TimeUtils.BeginningOfUnixTime, ResponseBody = null }; // insert it await store.ExecuteOperationAsync(Operation.Insert(lookupTable, reportHandle, reportHandle, avertTransactionEntity), storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Insert push registrations /// </summary> /// <param name="storageConsistencyMode">Consistency mode</param> /// <param name="userHandle">User handle</param> /// <param name="appHandle">App handle</param> /// <param name="platformType">Platform type</param> /// <param name="registrationId">OS registration id</param> /// <param name="hubRegistrationId">Hub registration id</param> /// <param name="language">Client language</param> /// <param name="lastUpdatedTime">Last updated time</param> /// <returns>Insert push registration task</returns> public async Task InsertPushRegistration( StorageConsistencyMode storageConsistencyMode, string userHandle, string appHandle, PlatformType platformType, string registrationId, string hubRegistrationId, string language, DateTime lastUpdatedTime) { PushRegistrationFeedEntity entity = new PushRegistrationFeedEntity() { UserHandle = userHandle, AppHandle = appHandle, PlatformType = platformType, OSRegistrationId = registrationId, HubRegistrationId = hubRegistrationId, Language = language, LastUpdatedTime = lastUpdatedTime }; CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.PushRegistrations); FeedTable table = this.tableStoreManager.GetTable(ContainerIdentifier.PushRegistrations, TableIdentifier.PushRegistrationsFeed) as FeedTable; Operation operation = Operation.InsertOrReplace(table, userHandle, appHandle, registrationId, entity); await store.ExecuteOperationAsync(operation, storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Insert reply /// </summary> /// <param name="storageConsistencyMode">Storage consistency mode</param> /// <param name="replyHandle">Reply handle</param> /// <param name="commentHandle">Comment handle</param> /// <param name="topicHandle">Topic handle</param> /// <param name="text">Reply text</param> /// <param name="language">Reply language</param> /// <param name="userHandle">User handle</param> /// <param name="createdTime">Created time</param> /// <param name="reviewStatus">Review status</param> /// <param name="appHandle">App handle</param> /// <param name="requestId">Request id associated with the create reply request</param> /// <returns>Insert reply task</returns> public async Task InsertReply( StorageConsistencyMode storageConsistencyMode, string replyHandle, string commentHandle, string topicHandle, string text, string language, string userHandle, DateTime createdTime, ReviewStatus reviewStatus, string appHandle, string requestId) { ReplyEntity replyEntity = new ReplyEntity() { CommentHandle = commentHandle, TopicHandle = topicHandle, Text = text, Language = language, UserHandle = userHandle, CreatedTime = createdTime, LastUpdatedTime = createdTime, AppHandle = appHandle, ReviewStatus = reviewStatus, RequestId = requestId }; CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.Replies); ObjectTable table = this.tableStoreManager.GetTable(ContainerIdentifier.Replies, TableIdentifier.RepliesObject) as ObjectTable; Operation operation = Operation.Insert(table, replyHandle, replyHandle, replyEntity); await store.ExecuteOperationAsync(operation, storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Query popular topics today /// </summary> /// <param name="timeRange">Time range</param> /// <param name="hostAppHandle">Hosting app handle: Master app or client app</param> /// <param name="cursor">Read cursor</param> /// <param name="limit">Number of items to return</param> /// <returns>Topic feed entities</returns> public async Task <IList <ITopicFeedEntity> > QueryPopularTopics(TimeRange timeRange, string hostAppHandle, int cursor, int limit) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.PopularTopics); RankFeedTable table = this.tableStoreManager.GetTable(ContainerIdentifier.PopularTopics, TableIdentifier.PopularTopicsFeed) as RankFeedTable; string feedKey = this.GetPopularTopicsFeedKey(timeRange, hostAppHandle); // convert our current API cursor (an int) to a CTStore cursor (a string). Note that CTStore cursors // are offset by 1 from API cursors. This hack will be removed when we change our API to use string-based // cursors. string ctCursor; if (cursor == 0) { ctCursor = null; } else { ctCursor = (cursor - 1).ToString(); } var result = await store.QueryRankFeedReverseAsync(table, ContainerIdentifier.PopularTopics.ToString(), feedKey, ctCursor, limit); var convertedResults = result.Select(item => StoreSerializers.MinimalTopicRankFeedEntityDeserialize(item.ItemKey)); return(convertedResults.ToList <ITopicFeedEntity>()); }
/// <summary> /// Updates content moderation entity /// </summary> /// <param name="storageConsistencyMode">consistency to use</param> /// <param name="moderationHandle">uniquely identifies this moderation request</param> /// <param name="moderationStatus">Moderation status</param> /// <param name="entity"><see cref="IModerationEntity"/> to update</param> /// <returns>Content moderation entity update task</returns> public async Task UpdateModerationStatus( StorageConsistencyMode storageConsistencyMode, string moderationHandle, ModerationStatus moderationStatus, IModerationEntity entity) { if (string.IsNullOrWhiteSpace(moderationHandle)) { throw new ArgumentNullException("moderationHandle"); } if (entity == null) { throw new ArgumentNullException("entity"); } // get content moderation table CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.Moderation); ObjectTable lookupTable = this.tableStoreManager.GetTable( ContainerIdentifier.Moderation, TableIdentifier.ModerationObject) as ObjectTable; // update the entity entity.ModerationStatus = moderationStatus; // update it in the store var operation = Operation.Replace(lookupTable, entity.AppHandle, moderationHandle, entity as ModerationEntity); await store.ExecuteOperationAsync(operation, storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Insert following activity /// </summary> /// <param name="storageConsistencyMode">Storage consistency mode</param> /// <param name="userHandle">User handle</param> /// <param name="appHandle">App handle</param> /// <param name="activityHandle">Activity handle</param> /// <param name="activityType">Activity type</param> /// <param name="actorUserHandle">Actor user handle</param> /// <param name="actedOnUserHandle">Acted on user handle</param> /// <param name="actedOnContentType">Acted on content type</param> /// <param name="actedOnContentHandle">Acted on content handle</param> /// <param name="createdTime">Created time</param> /// <returns>Insert activity task</returns> public async Task InsertFollowingActivity( StorageConsistencyMode storageConsistencyMode, string userHandle, string appHandle, string activityHandle, ActivityType activityType, string actorUserHandle, string actedOnUserHandle, ContentType actedOnContentType, string actedOnContentHandle, DateTime createdTime) { // Insert an activity into a user's following activities feed // // PartitionKey: UserHandle // FeedKey: AppHandle // ItemKey: ActivityHandle (reverse sorted by time). We use LikeHandle, CommentHandle, ReplyHandle, and RelationshipHandle as activity handles ActivityFeedEntity activityFeedEntity = new ActivityFeedEntity() { ActivityHandle = activityHandle, AppHandle = appHandle, ActivityType = activityType, ActorUserHandle = actorUserHandle, ActedOnUserHandle = actedOnUserHandle, ActedOnContentType = actedOnContentType, ActedOnContentHandle = actedOnContentHandle, CreatedTime = createdTime }; CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.FollowingActivities); FeedTable feedTable = this.tableStoreManager.GetTable(ContainerIdentifier.FollowingActivities, TableIdentifier.FollowingActivitiesFeed) as FeedTable; Operation operation = Operation.InsertOrReplace(feedTable, userHandle, appHandle, activityHandle, activityFeedEntity); await store.ExecuteOperationAsync(operation, storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Has the reporting user already complained about this user? /// </summary> /// <param name="appHandle">uniquely identifies the app that the users are in</param> /// <param name="reportedUserHandle">uniquely identifies the user who is being reported</param> /// <param name="reportingUserHandle">uniquely identifies the user doing the reporting</param> /// <returns>true if this user has previously complained about this user</returns> public async Task <bool> HasReportingUserReportedUserBefore(string appHandle, string reportedUserHandle, string reportingUserHandle) { // check the inputs if (string.IsNullOrWhiteSpace(appHandle)) { throw new ArgumentNullException("appHandle"); } else if (string.IsNullOrWhiteSpace(reportedUserHandle)) { throw new ArgumentNullException("reportedUserHandle"); } else if (string.IsNullOrWhiteSpace(reportingUserHandle)) { throw new ArgumentNullException("reportingUserHandle"); } // get the table interface CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.UserReports); ObjectTable lookupUniquenessTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserReports, TableIdentifier.UserReportsLookupUniquenessByReportingUser) as ObjectTable; // check if the reporting user has previously reported this user string uniquenessKey = UniquenessObjectKey(reportedUserHandle, reportingUserHandle); ObjectEntity uniquenessObject = await store.QueryObjectAsync <ObjectEntity>(lookupUniquenessTable, appHandle, uniquenessKey); if (uniquenessObject == null) { return(false); } return(true); }
/// <summary> /// Insert a new user-generated report of another user into the store /// </summary> /// <param name="storageConsistencyMode">consistency to use</param> /// <param name="reportHandle">uniquely identifies this report</param> /// <param name="reportedUserHandle">uniquely identifies the user who is being reported</param> /// <param name="reportingUserHandle">uniquely identifies the user doing the reporting</param> /// <param name="appHandle">uniquely identifies the app that the user is in</param> /// <param name="reason">the complaint against the content</param> /// <param name="lastUpdatedTime">when the report was received</param> /// <param name="hasComplainedBefore">has the reporting user complained about this user before?</param> /// <returns>a task that inserts the report into the store</returns> public async Task InsertUserReport( StorageConsistencyMode storageConsistencyMode, string reportHandle, string reportedUserHandle, string reportingUserHandle, string appHandle, ReportReason reason, DateTime lastUpdatedTime, bool hasComplainedBefore) { // get all the table interfaces CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.UserReports); ObjectTable lookupTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserReports, TableIdentifier.UserReportsLookup) as ObjectTable; ObjectTable lookupUniquenessTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserReports, TableIdentifier.UserReportsLookupUniquenessByReportingUser) as ObjectTable; FeedTable feedByAppTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserReports, TableIdentifier.UserReportsRecentFeedByApp) as FeedTable; FeedTable feedByReportedUserTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserReports, TableIdentifier.UserReportsRecentFeedByReportedUser) as FeedTable; FeedTable feedByReportingUserTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserReports, TableIdentifier.UserReportsRecentFeedByReportingUser) as FeedTable; CountTable countByReportedUserTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserReports, TableIdentifier.UserReportsCountByReportedUser) as CountTable; CountTable countByReportingUserTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserReports, TableIdentifier.UserReportsCountByReportingUser) as CountTable; // create the two entities that will be inserted into the tables UserReportEntity userReportEntity = new UserReportEntity() { ReportedUserHandle = reportedUserHandle, ReportingUserHandle = reportingUserHandle, AppHandle = appHandle, Reason = reason, CreatedTime = lastUpdatedTime }; UserReportFeedEntity userReportFeedEntity = new UserReportFeedEntity() { ReportHandle = reportHandle, ReportedUserHandle = reportedUserHandle, ReportingUserHandle = reportingUserHandle, AppHandle = appHandle }; // do the inserts and increments as a transaction Transaction transaction = new Transaction(); // the partition key is app handle for all tables so that a transaction can be achieved transaction.Add(Operation.Insert(lookupTable, appHandle, reportHandle, userReportEntity)); transaction.Add(Operation.Insert(feedByAppTable, appHandle, appHandle, reportHandle, userReportFeedEntity)); transaction.Add(Operation.Insert(feedByReportedUserTable, appHandle, reportedUserHandle, reportHandle, userReportFeedEntity)); transaction.Add(Operation.Insert(feedByReportingUserTable, appHandle, reportingUserHandle, reportHandle, userReportFeedEntity)); // if the reporting user has not previously reported this user, then increment counts if (!hasComplainedBefore) { string uniquenessKey = UniquenessObjectKey(reportedUserHandle, reportingUserHandle); transaction.Add(Operation.Insert(lookupUniquenessTable, appHandle, uniquenessKey, new ObjectEntity())); transaction.Add(Operation.InsertOrIncrement(countByReportedUserTable, appHandle, reportedUserHandle)); transaction.Add(Operation.InsertOrIncrement(countByReportingUserTable, appHandle, reportingUserHandle)); } // execute the transaction await store.ExecuteTransactionAsync(transaction, storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Upgrade the store version number in persistent redis /// </summary> /// <param name="ctStoreMananger">ctstore manager</param> /// <param name="persistentCacheConnectionString">connection string for persistent cache</param> /// <returns>true if upgrade is successful</returns> private static async Task <bool> UpgradeStoreVersionRedis(CTStoreManager ctStoreMananger, string persistentCacheConnectionString) { RedisCache redisCachePersistent = new RedisCache(persistentCacheConnectionString); CTStore persistentCacheStore = new CTStore(null, redisCachePersistent); ObjectTable versionTable = Table.GetObjectTable( ContainerIdentifier.ServiceConfig.ToString(), ctStoreMananger.ServiceConfigContainerInitials, ctStoreMananger.StoreVersionTableName, ctStoreMananger.StoreVersionTableInitials, StorageMode.CacheOnly); var persistentCacheVersion = await persistentCacheStore.QueryObjectAsync <StoreVersionEntity>(versionTable, ctStoreMananger.StoreVersionKey, ctStoreMananger.StoreVersionKey); // if version in store does not match oldVersion, then refuse to upgrade if (persistentCacheVersion.Version != oldVersion) { Console.WriteLine("Version mismatch in persistent Redis: original version in store is {0}", persistentCacheVersion.Version); return(false); } persistentCacheVersion.Version = newVersion; var operation = Operation.Replace(versionTable, ctStoreMananger.StoreVersionKey, ctStoreMananger.StoreVersionKey, persistentCacheVersion); // perform the insert operation on persistent redis await persistentCacheStore.ExecuteOperationAsync(operation, ConsistencyMode.Strong); return(true); }
/// <summary> /// Provision the contents of the redis caches /// </summary> /// <param name="persistentCacheConnectionString">connection string for persistent cache</param> /// <param name="tableStoreManager">table store manager</param> /// <returns>provisioning task</returns> private static async Task ProvisionRedisCaches(string persistentCacheConnectionString, CTStoreManager tableStoreManager) { Console.WriteLine("Provisioning Redis Caches..."); // insert the store version number into table storage var versionEntity = new StoreVersionEntity { Version = tableStoreManager.StoreVersionString }; ObjectTable versionTable = Table.GetObjectTable( ContainerIdentifier.ServiceConfig.ToString(), tableStoreManager.ServiceConfigContainerInitials, tableStoreManager.StoreVersionTableName, tableStoreManager.StoreVersionTableInitials, StorageMode.CacheOnly); var operation = Operation.Insert(versionTable, tableStoreManager.StoreVersionKey, tableStoreManager.StoreVersionKey, versionEntity); RedisCache redisCachePersistent = new RedisCache(persistentCacheConnectionString); CTStore persistentCache = new CTStore(null, redisCachePersistent); // perform the insert operation on persistent redis // note that we only insert the version number into persistent redis, // because with volatile redis each time the cache restarts the version number will be lost await persistentCache.ExecuteOperationAsync(operation, ConsistencyMode.Strong); }
/// <summary> /// Deletes all the Azure tables /// </summary> /// <param name="azureTableStorageConnectionString">connection string to azure table storage</param> /// <returns>delete task</returns> private static async Task DeleteAzureTables(string azureTableStorageConnectionString) { Console.WriteLine("Deleting all tables from Azure Table Store..."); // Get azure table storage with the give connection string AzureTableStorage azureTableStorage = new AzureTableStorage(azureTableStorageConnectionString); // Get CTStore using only azure table storage CTStore store = new CTStore(azureTableStorage, null); // Enumerate all the containers defined foreach (ContainerIdentifier containerIdentifier in Enum.GetValues(typeof(ContainerIdentifier))) { if (!ContainerTableDescriptorProvider.Containers.ContainsKey(containerIdentifier)) { Console.WriteLine(" " + containerIdentifier.ToString() + " - Descriptor not found"); continue; } // delete each table container await store.DeleteContainerAsync(containerIdentifier.ToString()); Console.WriteLine(" " + containerIdentifier.ToString() + " - Table Container Deleted"); } // clean up the StoreVersion container because it has no descriptor await store.DeleteContainerAsync(ContainerIdentifier.ServiceConfig.ToString()); Console.WriteLine(" " + ContainerIdentifier.ServiceConfig.ToString() + " - Table Container Deleted"); }
/// <summary> /// Upgrade the store version number in azure table storage /// </summary> /// <param name="tableStoreManager">table store manager</param> /// <param name="azureConnectionString">azure connection string</param> /// <returns>true if upgrade is successful</returns> private static async Task <bool> UpgradeStoreVersionAzureTables(CTStoreManager tableStoreManager, string azureConnectionString) { // Get azure table storage with the give connection string AzureTableStorage azureTableStorage = new AzureTableStorage(azureConnectionString); // Get CTStore using the azure table storage CTStore store = new CTStore(azureTableStorage, null); ObjectTable versionTable = Table.GetObjectTable( ContainerIdentifier.ServiceConfig.ToString(), tableStoreManager.ServiceConfigContainerInitials, tableStoreManager.StoreVersionTableName, tableStoreManager.StoreVersionTableInitials, StorageMode.PersistentOnly); var currentTableVersion = await azureTableStorage.QueryObjectAsync <StoreVersionEntity>(versionTable, tableStoreManager.StoreVersionKey, tableStoreManager.StoreVersionKey); // if version in store does not match oldVersion, then refuse to upgrade if (currentTableVersion.Version != oldVersion) { Console.WriteLine("Version mismatch in Azure table storage: original version in store is {0}", currentTableVersion.Version); return(false); } currentTableVersion.Version = newVersion; var operation = Operation.Replace(versionTable, tableStoreManager.StoreVersionKey, tableStoreManager.StoreVersionKey, currentTableVersion); // perform the insert on Azure table storage await store.ExecuteOperationAsync(operation, ConsistencyMode.Strong); return(true); }
/// <summary> /// Get store from container identifier /// </summary> /// <param name="containerIdentifier">Container identifier</param> /// <returns>CT store</returns> public async Task <CTStore> GetStore(ContainerIdentifier containerIdentifier) { // refuse to provide the store if the version check fails, or if it has not been executed if (this.initialized == false) { return(null); } ContainerDescriptor containerDescriptor = ContainerTableDescriptorProvider.Containers[containerIdentifier]; string azureStorageConnectionString = await this.connectionStringProvider.GetTablesAzureStorageConnectionString(containerDescriptor.AzureStorageInstanceType); string redisConnectionString = await this.connectionStringProvider.GetRedisConnectionString(containerDescriptor.RedisInstanceType); string uniqueStoreIdentity = string.Join(":", azureStorageConnectionString, redisConnectionString); // cachedStoreObjects is a thread-safe dictionary (ConcurrentDictionary). If uniqueStoreIdentity is not present // in cachedStoreObects, try adding it. Since GetStore can be called concurrently by // different threads, it is possible for two (or more) threads to attempt inserting uniqueStoreIdentity // concurrently in the cachedStoreObjects. That's ok, because the call to TryAdd is guaranteed to be thread-safe. // One of the threads will not be able to insert (i.e., TryAdd will return false), but the code will happily execute // and fall through to the return statement. // This code makes no use of locking on the common path (i.e., reads of cachedStoreObjects). if (!this.cachedStoreObjects.ContainsKey(uniqueStoreIdentity)) { AzureTableStorage azureTableStorage = new AzureTableStorage(azureStorageConnectionString); azureTableStorage.TableRequestOptions = AzureStorageConfiguration.GetTableRequestOptions(); RedisCache redisCache = new RedisCache(redisConnectionString); CTStore store = new CTStore(azureTableStorage, redisCache); this.cachedStoreObjects.TryAdd(uniqueStoreIdentity, store); } return(this.cachedStoreObjects[uniqueStoreIdentity]); }
/// <summary> /// Update following relationship. /// Following user : someone who i am following. /// </summary> /// <param name="storageConsistencyMode">Consistency mode</param> /// <param name="relationshipHandle">Relationship handle</param> /// <param name="userHandle">User handle</param> /// <param name="relationshipUserHandle">Relationship user handle</param> /// <param name="appHandle">App handle</param> /// <param name="userRelationshipStatus">User relationship status</param> /// <param name="lastUpdatedTime">Last updated time</param> /// <param name="readUserRelationshipLookupEntity">Read user relationship lookup entity</param> /// <returns>Update following relationship task</returns> public async Task UpdateFollowingRelationship( StorageConsistencyMode storageConsistencyMode, string relationshipHandle, string userHandle, string relationshipUserHandle, string appHandle, UserRelationshipStatus userRelationshipStatus, DateTime lastUpdatedTime, IUserRelationshipLookupEntity readUserRelationshipLookupEntity) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.UserFollowing); ObjectTable lookupTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserFollowing, TableIdentifier.FollowingLookup) as ObjectTable; FeedTable feedTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserFollowing, TableIdentifier.FollowingFeed) as FeedTable; CountTable countTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserFollowing, TableIdentifier.FollowingCount) as CountTable; await this.UpdateUserRelationship( storageConsistencyMode, relationshipHandle, userHandle, relationshipUserHandle, appHandle, userRelationshipStatus, lastUpdatedTime, readUserRelationshipLookupEntity, store, lookupTable, feedTable, countTable); }
/// <summary> /// Update notifications status /// </summary> /// <param name="storageConsistencyMode">Storage consistency mode</param> /// <param name="userHandle">User handle</param> /// <param name="appHandle">App handle</param> /// <param name="readActivityHandle">Read activity handle</param> /// <param name="readNotificationsStatusEntity">Read notifications status entity</param> /// <returns>Update notifications status task</returns> public async Task UpdateNotificationsStatus( StorageConsistencyMode storageConsistencyMode, string userHandle, string appHandle, string readActivityHandle, INotificationsStatusEntity readNotificationsStatusEntity) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.Notifications); ObjectTable statusTable = this.tableStoreManager.GetTable(ContainerIdentifier.Notifications, TableIdentifier.NotificationsStatus) as ObjectTable; CountTable countTable = this.tableStoreManager.GetTable(ContainerIdentifier.Notifications, TableIdentifier.NotificationsCount) as CountTable; Transaction transaction = new Transaction(); if (readNotificationsStatusEntity == null) { NotificationsStatusEntity notificationsStatusEntity = new NotificationsStatusEntity() { ReadActivityHandle = readActivityHandle }; transaction.Add(Operation.Insert(statusTable, userHandle, appHandle, notificationsStatusEntity)); } else { readNotificationsStatusEntity.ReadActivityHandle = readActivityHandle; transaction.Add(Operation.Replace(statusTable, userHandle, appHandle, readNotificationsStatusEntity as NotificationsStatusEntity)); } transaction.Add(Operation.InsertOrReplace(countTable, userHandle, appHandle, 0)); await store.ExecuteTransactionAsync(transaction, storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Insert user topic /// </summary> /// <param name="storageConsistencyMode">Storage consistency mode</param> /// <param name="userHandle">User handle</param> /// <param name="appHandle">App handle</param> /// <param name="topicHandle">Topic handle</param> /// <returns>Insert user topic task</returns> public async Task InsertUserTopic( StorageConsistencyMode storageConsistencyMode, string userHandle, string appHandle, string topicHandle) { TopicFeedEntity topicFeedEntity = new TopicFeedEntity() { AppHandle = appHandle, TopicHandle = topicHandle, UserHandle = userHandle }; CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.UserTopics); FeedTable feedTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserTopics, TableIdentifier.UserTopicsFeed) as FeedTable; CountTable countTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserTopics, TableIdentifier.UserTopicsCount) as CountTable; Transaction transaction = new Transaction(); transaction.Add(Operation.Insert(feedTable, userHandle, appHandle, topicHandle, topicFeedEntity)); transaction.Add(Operation.Insert(feedTable, userHandle, MasterApp.AppHandle, topicHandle, topicFeedEntity)); transaction.Add(Operation.InsertOrIncrement(countTable, userHandle, appHandle)); transaction.Add(Operation.InsertOrIncrement(countTable, userHandle, MasterApp.AppHandle)); await store.ExecuteTransactionAsync(transaction, storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Query notification /// </summary> /// <param name="userHandle">User handle</param> /// <param name="appHandle">App handle</param> /// <param name="activityHandle">Activity handle</param> /// <returns>Activity feed entity</returns> public async Task <IActivityFeedEntity> QueryNotification(string userHandle, string appHandle, string activityHandle) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.Notifications); FeedTable table = this.tableStoreManager.GetTable(ContainerIdentifier.Notifications, TableIdentifier.NotificationsFeed) as FeedTable; return(await store.QueryFeedItemAsync <ActivityFeedEntity>(table, userHandle, appHandle, activityHandle)); }
/// <summary> /// Query like /// </summary> /// <param name="contentHandle">Content handle</param> /// <param name="userHandle">User handle</param> /// <returns>Like lookup entity</returns> public async Task <ILikeLookupEntity> QueryLike(string contentHandle, string userHandle) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.Likes); ObjectTable lookupTable = this.tableStoreManager.GetTable(ContainerIdentifier.Likes, TableIdentifier.LikesLookup) as ObjectTable; return(await store.QueryObjectAsync <LikeLookupEntity>(lookupTable, contentHandle, userHandle)); }
/// <summary> /// Query app key index /// </summary> /// <param name="appKey">App key</param> /// <returns>App lookup entity</returns> public async Task <IAppLookupEntity> QueryAppKeyIndex(string appKey) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.AppKeys); ObjectTable table = this.tableStoreManager.GetTable(ContainerIdentifier.AppKeys, TableIdentifier.AppKeysIndex) as ObjectTable; return(await store.QueryObjectAsync <AppLookupEntity>(table, appKey, appKey)); }
/// <summary> /// Query pin /// </summary> /// <param name="userHandle">User handle</param> /// <param name="topicHandle">Topic handle</param> /// <returns>Pin lookup entity</returns> public async Task <IPinLookupEntity> QueryPin(string userHandle, string topicHandle) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.Pins); ObjectTable lookupTable = this.tableStoreManager.GetTable(ContainerIdentifier.Pins, TableIdentifier.PinsLookup) as ObjectTable; return(await store.QueryObjectAsync <PinLookupEntity>(lookupTable, userHandle, topicHandle)); }
/// <summary> /// Query popular topics maximum count /// </summary> /// <param name="timeRange">Time range</param> /// <returns>Maximum count of popular topics feeds</returns> public async Task <long> QueryPopularTopicsMaxCount(TimeRange timeRange) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.PopularTopics); RankFeedTable table = this.tableStoreManager.GetTable(ContainerIdentifier.PopularTopics, TableIdentifier.PopularTopicsFeed) as RankFeedTable; return(table.MaxFeedSizeInCache); }
/// <summary> /// Update a comment /// </summary> /// <param name="storageConsistencyMode">Storage consistency mode</param> /// <param name="commentHandle">Comment handle</param> /// <param name="commentEntity">Comment entity</param> /// <returns>Update comment task</returns> public async Task UpdateComment(StorageConsistencyMode storageConsistencyMode, string commentHandle, ICommentEntity commentEntity) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.Comments); ObjectTable table = this.tableStoreManager.GetTable(ContainerIdentifier.Comments, TableIdentifier.CommentsObject) as ObjectTable; Operation operation = Operation.Replace(table, commentHandle, commentHandle, commentEntity as CommentEntity); await store.ExecuteOperationAsync(operation, storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Delete blob metadata /// </summary> /// <param name="storageConsistencyMode">Storage consistency mode</param> /// <param name="appHandle">App handle</param> /// <param name="userHandle">User handle</param> /// <param name="blobHandle">Blob handle</param> /// <returns>Delete blob metadata task</returns> public async Task DeleteBlobMetadata(StorageConsistencyMode storageConsistencyMode, string appHandle, string userHandle, string blobHandle) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.Blobs); ObjectTable table = this.tableStoreManager.GetTable(ContainerIdentifier.Blobs, TableIdentifier.BlobsMetadata) as ObjectTable; string partitionKey = this.GetPartitionKey(appHandle, userHandle); Operation operation = Operation.Delete(table, partitionKey, blobHandle); await store.ExecuteOperationAsync(operation, storageConsistencyMode.ToConsistencyMode()); }
/// <summary> /// Query client config /// </summary> /// <param name="developerId">Developer id</param> /// <param name="clientName">Client name</param> /// <returns>Client config entity</returns> public async Task <IClientConfigEntity> QueryClientConfig(string developerId, string clientName) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.ClientConfigs); ObjectTable clientNameTable = this.tableStoreManager.GetTable(ContainerIdentifier.ClientConfigs, TableIdentifier.ClientConfigsObject) as ObjectTable; var objectKey = this.GetClientConfigsObjectKey(developerId, clientName); return(await store.QueryObjectAsync <ClientConfigEntity>(clientNameTable, objectKey, objectKey)); }
/// <summary> /// Query client name from ClientNamesFeed in AppKey container /// </summary> /// <param name="appKey">App key</param> /// <returns>Client names</returns> public async Task <IList <IClientNamesFeedEntity> > QueryClientNames(string appKey) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.AppKeys); FeedTable table = this.tableStoreManager.GetTable(ContainerIdentifier.AppKeys, TableIdentifier.ClientNamesFeed) as FeedTable; var result = await store.QueryFeedAsync <ClientNamesFeedEntity>(table, appKey, this.tableStoreManager.DefaultFeedKey, null, int.MaxValue); return(result.ToList <IClientNamesFeedEntity>()); }
/// <summary> /// Query user apps /// </summary> /// <param name="userHandle">User handle</param> /// <returns>Apps used by user</returns> public async Task <IList <IAppFeedEntity> > QueryUserApps(string userHandle) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.Users); FeedTable indexTable = this.tableStoreManager.GetTable(ContainerIdentifier.Users, TableIdentifier.UserAppsFeed) as FeedTable; var result = await store.QueryFeedAsync <AppFeedEntity>(indexTable, userHandle, this.tableStoreManager.DefaultFeedKey, null, int.MaxValue); return(result.ToList <IAppFeedEntity>()); }
/// <summary> /// Query user /// </summary> /// <param name="userHandle">User handle</param> /// <param name="appHandle">App handle</param> /// <returns>User profile entity</returns> public async Task <IUserProfileEntity> QueryUserProfile(string userHandle, string appHandle) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.Users); ObjectTable profilesTable = this.tableStoreManager.GetTable(ContainerIdentifier.Users, TableIdentifier.UserProfilesObject) as ObjectTable; UserProfileEntity userProfileEntity = await store.QueryObjectAsync <UserProfileEntity>(profilesTable, userHandle, appHandle); return(userProfileEntity); }
/// <summary> /// Query likes for a content /// </summary> /// <param name="contentHandle">Content handle</param> /// <param name="cursor">Read cursor</param> /// <param name="limit">Number of items to return</param> /// <returns>List of like feed entities</returns> public async Task <IList <ILikeFeedEntity> > QueryLikes(string contentHandle, string cursor, int limit) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.Likes); FeedTable feedTable = this.tableStoreManager.GetTable(ContainerIdentifier.Likes, TableIdentifier.LikesFeed) as FeedTable; var result = await store.QueryFeedAsync <LikeFeedEntity>(feedTable, contentHandle, this.tableStoreManager.DefaultFeedKey, cursor, limit); return(result.ToList <ILikeFeedEntity>()); }