From a467b556c1a4ed78523b738e8de312c20dbbae9e Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 15 Oct 2010 18:30:08 +0000 Subject: [PATCH] See ChangeLog Monotone-Parent: 5bef03fdbb4e4f3112fabf6812f1a2e0932c85a9 Monotone-Revision: 785d7cb3b05b0317ec2ce52a1f17d6c130bf53fb Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2010-10-15T18:30:08 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 9 ++++ NEWS | 3 ++ SoObjects/SOGo/SOGoCache.h | 5 ++ SoObjects/SOGo/SOGoCache.m | 85 +++++++++++++++++++++++++++------- SoObjects/SOGo/SOGoGCSFolder.h | 8 ++-- SoObjects/SOGo/SOGoGCSFolder.m | 42 +++++++++++------ SoObjects/SOGo/SOGoUser.h | 1 - 7 files changed, 117 insertions(+), 36 deletions(-) diff --git a/ChangeLog b/ChangeLog index 22d9536fe..41ad037a7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2010-10-15 Ludovic Marcotte + + * SoObjects/SOGo/SOGoGCSFolder.m - remove the ACL cache + and moved it to SOGoCache with memcached support. Also + modified _setDisplayNameFromRow: to NOT use SOGoUser + instances and it is _very costly_ to do so. + * SoObjects/SOGo/SOGoCache.{h,m} - added support for + caching ACLs locally and in memcached + 2010-10-15 Wolfgang Sourdeau * OpenChange/MAPIStoreContext.m (-getPath:ofFMID:withTableType:): diff --git a/NEWS b/NEWS index 18216f7a4..093cd52e4 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,9 @@ - improved groups support by caching results - updated the Polish, Italian and Ukrainian translations - added the capability of renaming subscribed address books +- acls are now cached in memcached and added a major performance + improvement when listing calendar / contact folders + 1.3-20100921 (1.3.2) -------------------- diff --git a/SoObjects/SOGo/SOGoCache.h b/SoObjects/SOGo/SOGoCache.h index 2845892c4..7558f818b 100644 --- a/SoObjects/SOGo/SOGoCache.h +++ b/SoObjects/SOGo/SOGoCache.h @@ -103,6 +103,11 @@ - (void) setCASPGTId: (NSString *) pgtId forPGTIOU: (NSString *) pgtIou; +- (void) setACLs: (NSDictionary *) theACLs + forPath: (NSString *) thePath; + +- (NSMutableDictionary *) aclsForPath: (NSString *) thePath; + @end #endif /* SOGOCACHE_H */ diff --git a/SoObjects/SOGo/SOGoCache.m b/SoObjects/SOGo/SOGoCache.m index 7c6df265a..9b8b8203b 100644 --- a/SoObjects/SOGo/SOGoCache.m +++ b/SoObjects/SOGo/SOGoCache.m @@ -25,9 +25,16 @@ * [ Cache Structure ] * * users value = instances of SOGoUser > flushed after the completion of every SOGo requests + * groups value = instances of SOGoGroup > flushed after the completion of every SOGo requests + * imap4Connections value = + * localCache value = any value of what's in memcached - this is used to NOT query memcached within the same sogod instance + * + * [ Distributed (using memcached) cache structure ] + * * +defaults value = NSDictionary instance > user's defaults * +settings value = NSDictionary instance > user's settings * +attributes value = NSMutableDictionary instance > user's LDAP attributes + * +acl value = NSDictionary instance > ACLs on an object at specified path * + value = NSString instance (array components separated by ",") or group member logins for a specific group in domain */ @@ -41,6 +48,7 @@ #import #import +#import "NSDictionary+BSJSONAdditions.h" #import "SOGoObject.h" #import "SOGoSystemDefaults.h" #import "SOGoUser.h" @@ -262,10 +270,10 @@ static memcached_st *handle = NULL; { keyData = [key dataUsingEncoding: NSUTF8StringEncoding]; valueData = [value dataUsingEncoding: NSUTF8StringEncoding]; - error = memcached_set (handle, - [keyData bytes], [keyData length], - [valueData bytes], [valueData length], - expiration, 0); + error = memcached_set(handle, + [keyData bytes], [keyData length], + [valueData bytes], [valueData length], + expiration, 0); if (error != MEMCACHED_SUCCESS) [self logWithFormat: @"an error occurred when caching value for key '%@':" @@ -327,6 +335,8 @@ static memcached_st *handle = NULL; NSData *keyData; memcached_return rc; + [localCache removeObjectForKey: key]; + if (handle) { keyData = [key dataUsingEncoding: NSUTF8StringEncoding]; @@ -343,25 +353,39 @@ static memcached_st *handle = NULL; key]; } + +// +// This method is used to cache a specific value (represented as a +// string, usually JSON stuff) using the specified key and type-suffix +// +// The final key will aways be of the form: + +// +// This method will insert the value in the localCache and also +// in memcached. +// - (void) _cacheValues: (NSString *) theAttributes ofType: (NSString *) theType - forLogin: (NSString *) theLogin + forKey: (NSString *) theKey { NSString *keyName; - keyName = [NSString stringWithFormat: @"%@+%@", theLogin, theType]; - [self setValue: theAttributes forKey: keyName]; - [localCache setObject: theAttributes forKey: keyName]; + keyName = [NSString stringWithFormat: @"%@+%@", theKey, theType]; + + if (theAttributes) + { + [self setValue: theAttributes forKey: keyName]; + [localCache setObject: theAttributes forKey: keyName]; + } } - (NSString *) _valuesOfType: (NSString *) theType - forLogin: (NSString *) theLogin + forKey: (NSString *) theKey { NSString *valueString, *keyName; valueString = nil; - keyName = [NSString stringWithFormat: @"%@+%@", theLogin, theType]; + keyName = [NSString stringWithFormat: @"%@+%@", theKey, theType]; valueString = [localCache objectForKey: keyName]; if (!valueString) { @@ -377,36 +401,36 @@ static memcached_st *handle = NULL; forLogin: (NSString *) login { [self _cacheValues: theAttributes ofType: @"attributes" - forLogin: login]; + forKey: login]; } - (NSString *) userAttributesForLogin: (NSString *) theLogin { - return [self _valuesOfType: @"attributes" forLogin: theLogin]; + return [self _valuesOfType: @"attributes" forKey: theLogin]; } - (void) setUserDefaults: (NSString *) theAttributes forLogin: (NSString *) login { [self _cacheValues: theAttributes ofType: @"defaults" - forLogin: login]; + forKey: login]; } - (NSString *) userDefaultsForLogin: (NSString *) theLogin { - return [self _valuesOfType: @"defaults" forLogin: theLogin]; + return [self _valuesOfType: @"defaults" forKey: theLogin]; } - (void) setUserSettings: (NSString *) theAttributes forLogin: (NSString *) login { [self _cacheValues: theAttributes ofType: @"settings" - forLogin: login]; + forKey: login]; } - (NSString *) userSettingsForLogin: (NSString *) theLogin { - return [self _valuesOfType: @"settings" forLogin: theLogin]; + return [self _valuesOfType: @"settings" forKey: theLogin]; } /* CAS session support */ @@ -452,4 +476,33 @@ static memcached_st *handle = NULL; forKey: [NSString stringWithFormat: @"cas-pgtiou:%@", pgtIou]]; } +// +// +// +- (void) setACLs: (NSDictionary *) theACLs + forPath: (NSString *) thePath +{ + NSLog(@"setting ACLs: %@ (%@) forPath: %@", [theACLs jsonStringValue], theACLs, thePath); + if (theACLs) + [self _cacheValues: [theACLs jsonStringValue] + ofType: @"acl" + forKey: thePath]; + else + [self removeValueForKey: [NSString stringWithFormat: @"%@+acl", thePath]]; +} + +- (NSMutableDictionary *) aclsForPath: (NSString *) thePath +{ + NSString *s; + + s = [self _valuesOfType: @"acl" forKey: thePath]; + + if (s) + return [NSMutableDictionary dictionaryWithJSONString: s]; + + return nil; +} + + + @end diff --git a/SoObjects/SOGo/SOGoGCSFolder.h b/SoObjects/SOGo/SOGoGCSFolder.h index 0456eee0a..73c578a7b 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.h +++ b/SoObjects/SOGo/SOGoGCSFolder.h @@ -1,14 +1,15 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG + Copyright (C) 2006-2010 Inverse inc. - This file is part of OpenGroupware.org. + This file is part of SOGo. - OGo is free software; you can redistribute it and/or modify it under + SOGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - OGo is distributed in the hope that it will be useful, but WITHOUT ANY + SOGo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. @@ -50,7 +51,6 @@ NSString *ocsPath; GCSFolder *ocsFolder; NSMutableDictionary *childRecords; - NSMutableDictionary *aclCache; BOOL userCanAccessAllObjects; /* i.e. user obtains 'Access Object' on subobjects */ } diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index 55063f63b..da976376d 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -72,6 +72,7 @@ #import "SOGoUser.h" #import "SOGoUserDefaults.h" #import "SOGoUserSettings.h" +#import "SOGoUserManager.h" #import "SOGoWebDAVAclManager.h" #import "WORequest+SOGo.h" #import "WOResponse+SOGo.h" @@ -198,7 +199,6 @@ static NSArray *childRecordFields = nil; { ocsPath = nil; ocsFolder = nil; - aclCache = [NSMutableDictionary new]; childRecords = [NSMutableDictionary new]; userCanAccessAllObjects = NO; } @@ -210,7 +210,6 @@ static NSArray *childRecordFields = nil; { [ocsFolder release]; [ocsPath release]; - [aclCache release]; [childRecords release]; [super dealloc]; } @@ -286,11 +285,14 @@ static NSArray *childRecordFields = nil; if (!activeUserIsOwner) { - ownerIdentity = [[SOGoUser userWithLogin: owner] - primaryIdentity]; - [displayName - appendString: [ownerIdentity keysWithFormat: - @" (%{fullName} <%{email}>)"]]; + // We MUST NOT use SOGoUser instances here (by calling -primaryIdentity) + // as it'll load user defaults and user settings which is _very costly_ + // since it involves JSON parsing and database requests + ownerIdentity = [[SOGoUserManager sharedUserManager] + contactInfosForUserWithUIDorEmail: owner]; + + [displayName appendFormat: @" (%@ <%@>)", [ownerIdentity objectForKey: @"cn"], + [ownerIdentity objectForKey: @"c_email"]]; } } } @@ -1480,17 +1482,23 @@ static NSArray *childRecordFields = nil; { NSMutableDictionary *aclsForObject; - aclsForObject = [aclCache objectForKey: objectPath]; + aclsForObject = [[SOGoCache sharedCache] aclsForPath: objectPath]; if (!aclsForObject) { aclsForObject = [NSMutableDictionary dictionary]; - [aclCache setObject: aclsForObject - forKey: objectPath]; } if (roles) - [aclsForObject setObject: roles forKey: uid]; + { + [aclsForObject setObject: roles forKey: uid]; + } else - [aclsForObject removeObjectForKey: uid]; + { + [aclsForObject removeObjectForKey: uid]; + } + + // We update our distributed cache + [[SOGoCache sharedCache] setACLs: aclsForObject + forPath: objectPath]; } - (NSArray *) _realAclsForUser: (NSString *) uid @@ -1501,7 +1509,7 @@ static NSArray *childRecordFields = nil; NSDictionary *aclsForObject; objectPath = [objectPathArray componentsJoinedByString: @"/"]; - aclsForObject = [aclCache objectForKey: objectPath]; + aclsForObject = [[SOGoCache sharedCache] aclsForPath: objectPath]; if (aclsForObject) acls = [aclsForObject objectForKey: uid]; else @@ -1571,9 +1579,13 @@ static NSArray *childRecordFields = nil; } } objectPath = [objectPathArray componentsJoinedByString: @"/"]; - aclsForObject = [aclCache objectForKey: objectPath]; + aclsForObject = [[SOGoCache sharedCache] aclsForPath: objectPath]; if (aclsForObject) - [aclsForObject removeObjectsForKeys: usersAndGroups]; + { + [aclsForObject removeObjectsForKeys: usersAndGroups]; + [[SOGoCache sharedCache] setACLs: aclsForObject + forPath: objectPath]; + } uids = [usersAndGroups componentsJoinedByString: @"') OR (c_uid = '"]; qs = [NSString stringWithFormat: @"(c_object = '/%@') AND ((c_uid = '%@'))", diff --git a/SoObjects/SOGo/SOGoUser.h b/SoObjects/SOGo/SOGoUser.h index 4cdddacaf..6552f49e6 100644 --- a/SoObjects/SOGo/SOGoUser.h +++ b/SoObjects/SOGo/SOGoUser.h @@ -70,7 +70,6 @@ NSArray *allEmails; NSMutableArray *mailAccounts; NSString *cn; - BOOL propagateCache; } // + (NSString *) language;